乐趣区

关于jvm:深入理解JVM-解读GC日志

深刻了解 JVM – 解读 GC 日志

前言

​ 这次的文章会依据实战来介绍如何看 JVM 的日志,看 JVM 日志说难也难,说容易也容易,更多的是须要工夫去一直的尝试进行总结。

​ 另外,因为代码的理论运行成果在不同的机器是不一样的!这篇文章应用的是jdk1.8.0_221 的版本,具体的系统配置查看:

概述:

​ 次要内容还是以解说如何浏览日志,同时不同的机器运行的后果不同,文章更多的是介绍如何解读参数:

参数配置案例

配置介绍:

  1. 新生代 5M
  2. 最大新生代内存 5M
  3. 初始化堆内存大小 10M
  4. 最大堆内存大小 10M
  5. 新生代 eden 区域大小,或者说 survior 配比:8 代表 8:1:1
  6. 应用 ParNew+CMS 收集器

实际操作:

不执行任何代码测试:

参数配置:

-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+UseSerialGC

参数的含意:

​ 这里给堆调配了 20M 的内存空间,新生代调配 10M,同时打印 GC 信息,新生代的分配比例为 8:1:1,最初应用 serrial 收集器。留神是 serrial 收集器。

代码配置

​ 先从最简略的办法开始,咱们运行一个没有任何代码的 Main 办法

public class MinorGcTest {public static void main(String[] args) {}}

留神上面的内容是运行一个空 Main 办法的日志内容。

Heap
 def new generation   total 9216K, used 3977K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  eden space 8192K,  48% used [0x00000000fec00000, 0x00000000fefe27f0, 0x00000000ff400000)
  from space 1024K,   0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
  to   space 1024K,   0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)
 tenured generation   total 10240K, used 0K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
   the space 10240K,   0% used [0x00000000ff600000, 0x00000000ff600000, 0x00000000ff600200, 0x0000000100000000)
 Metaspace       used 3241K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 349K, capacity 388K, committed 512K, reserved 1048576K

解释:

​ 首先看下第二行:def new generation total 9216K, used 3977K,很显著,阐明新生代总大小为 9216K 也就是 9M 的空间大小,为什么是 9M 呢,因为这里计算的是 8M 的 eden 区域 +1M 的 survior from 区域,剩下一个 survior to 的区域加起来一共 10M,而可用空间依据复制算法只有 9M 也是正确的。

​ 而后看下:eden space 8192K, 48% used,能够看到即便不运行任何的代码咱们也应用了 4M 左右的空间,那么这 4M 的空间是什么货色呢,这部分对象其实是 JVM 本身运行产生的一些对象,这里也会放到前面的文章进行解读。

Metaspace代表着元空间,因为 JDK8 没有了永恒代,所以 JDK8 之前的 JVM 看到的内容在这里是不一样的。

堆溢出测试:

​ 上面来看下堆溢出的状况下 GC 的日志打印了哪些内容,JAVA 异样的信息疏忽了,因为影响咱们看日志:

参数配置:

-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+UseSerialGC

代码配置:

byte[] allocation1, allocation2, allocation3, allocation4, allocation5;
allocation1 = new byte[_1MB * 2];
allocation1 = new byte[_1MB * 2];
allocation1 = new byte[_1MB * 1];
allocation3 = new byte[20*_1MB * 2];

上面是日志的后果:

[GC (Allocation Failure) [DefNew: 7909K->1023K(9216K), 0.0025258 secs] 7909K->3242K(19456K), 0.0025739 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [DefNew: 2131K->0K(9216K), 0.0015020 secs][Tenured: 4266K->2217K(10240K), 0.0030024 secs] 4350K->2217K(19456K), [Metaspace: 3254K->3254K(1056768K)], 0.0045414 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Allocation Failure) [Tenured: 2217K->2198K(10240K), 0.0017918 secs] 2217K->2198K(19456K), [Metaspace: 3254K->3254K(1056768K)], 0.0018074 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap
 def new generation   total 9216K, used 404K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  eden space 8192K,   4% used [0x00000000fec00000, 0x00000000fec65330, 0x00000000ff400000)
  from space 1024K,   0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
  to   space 1024K,   0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)
 tenured generation   total 10240K, used 2198K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
   the space 10240K,  21% used [0x00000000ff600000, 0x00000000ff8259d8, 0x00000000ff825a00, 0x0000000100000000)
 Metaspace       used 3360K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 362K, capacity 388K, committed 512K, reserved 1048576K
[GC (Allocation Failure) [DefNew: 7909K->1023K(9216K), 0.0025258 secs] 7909K->3242K(19456K), 0.0025739 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 

咱们先看一下第一行,这里应用的是 serrial 收集器,所以能够看到新生代打印 7909K->1023K(9216K),示意一共可用空间 9216KB,而垃圾此时占用了 7909K,回收之后剩下 1023K。第二局部:7909K->3242K(19456K),示意整个堆的回收状况,19456K 示意整个堆的可用空间,同样从堆大小能够看到从7909K 回收到剩下 3242K 的存活对象。最初一部分:[Times: user=0.00 sys=0.00, real=0.00 secs] 示意进展的工夫,以毫秒为单位,因为此次的垃圾回收工夫太短,所以没有金酸进去,user 示意用户线程进展工夫,sys 示意零碎回收工夫,real 示意理论的进展工夫。

​ 接着看一下 full gc 的日志,能够看到这里间接对于新生代和老年代回收内存之后发现简直没有空间残余,还是放不下 20M 的大对象,所以间接抛出了 oom。

[Full GC (Allocation Failure) [Tenured: 2217K->2198K(10240K), 0.0017918 secs] 2217K->2198K(19456K), [Metaspace: 3254K->3254K(1056768K)], 0.0018074 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap

parNew+cms 测试:

不执行任何测试

参数配置:

​ 留神最初应用了 parnew+cms 的组合

-verbose:gc
-Xms20M
-Xmx20M
-Xmn10M
-XX:+PrintGCDetails
-XX:SurvivorRatio=8
-XX:+UseParNewGC
-XX:+UseConcMarkSweepGC

代码配置:

​ 同样是不执行任何的代码,后果和其余的收集器相似

Heap
 par new generation   total 9216K, used 3977K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  eden space 8192K,  48% used [0x00000000fec00000, 0x00000000fefe27f0, 0x00000000ff400000)
  from space 1024K,   0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
  to   space 1024K,   0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)
 concurrent mark-sweep generation total 10240K, used 0K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
 Metaspace       used 3255K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 353K, capacity 388K, committed 512K, reserved 1048576K

如果去掉 cms?

​ 如果咱们把下面的参数去掉-XX:+UseConcMarkSweepGC,会呈现上面的内容:

Heap
 par new generation   total 9216K, used 3977K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  eden space 8192K,  48% used [0x00000000fec00000, 0x00000000fefe27f0, 0x00000000ff400000)
  from space 1024K,   0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
  to   space 1024K,   0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)
 tenured generation   total 10240K, used 0K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
   the space 10240K,   0% used [0x00000000ff600000, 0x00000000ff600000, 0x00000000ff600200, 0x0000000100000000)
 Metaspace       used 3238K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 349K, capacity 388K, committed 512K, reserved 1048576K
Java HotSpot(TM) 64-Bit Server VM warning: Using the ParNew young collector with the Serial old collector is deprecated and will likely be removed in a future release

这里留神一下最初一句:

​ 这里阐明的是不举荐应用 parNew+serrial 的组合,并且阐明在将来的版本中会废除这种组合,实际上在 JDK9 中就曾经齐全禁止 parnew+serrial 的组合了

Java HotSpot(TM) 64-Bit Server VM warning: Using the ParNew young collector with the Serial old collector is deprecated and will likely be removed in a future release

总结:

​ 依据下面的几个测试案例能够看到,浏览 GC 的日志还是比较简单的,然而理论运行又会发现因为各种因素的烦扰,理论运行的后果会和预期的后果不一样,这里不须要过多的放心,因为只有把握了根底的实践,在依据实际模仿一直的相熟会发现 JVM 的垃圾回收法则根本还是合乎咱们的实践根底介绍的。

​ 如果对于对象调配策略的感兴趣能够浏览之前集体的文章:深刻了解 JVM 虚拟机 – jvm 的对象调配策略

写在最初

​ 浏览日志倡议更多的是实操和练习,多尝试几遍之后更能加深记忆,因为集体机器自身不跑任何代码也会产生 4M 的对象,所以只能简略的介绍浏览日志的办法了 …..

退出移动版