GC日志怎么看

Young GC

案例准备

/**
 * 案例代码
 */
public class Demo1 {

    public static void main(String[] args) {
        // 1. 在eden区创建了一个1m数组对象,main线程虚拟机栈中
        // 的main()方法栈帧的局部变量arry1指向它
        byte[] arry1 = new byte[1024 * 1024];
        // 2. eden区中创建了一个新对象,arry1指向第二个对象
        arry1 = new byte[1024 * 1024];
        // 3. eden区中创建了一个新对象,arry1指向第三个对象
        arry1 = new byte[1024 * 1024];
        // 4. arry1不指向任何对象,3个对象没有gc root引用,变成了垃圾
        arry1 = null;
        // 5. 往eden区中创建一个2m对象,由于eden区最大只有4m,此时会触发young gc
        byte[] arry2 = new byte[2 * 1024 * 1024];
    }

}

/**
 * 执行脚本
 */
java  -jar -Xmn5M -Xms10M -Xmx10M -XX:SurvivorRatio=8 -XX:PretenureSizeThreshold=10M -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:gc.log  demo-1.0.jar

 

  首先我将新生代大小设置为5m,最大堆10m,eden和from、to比例为8:1:1 ;大对象阈值为10m(超过阈值直接进入老年代);垃圾回收器用的 parnew + cms。上面的代码肯定会young gc的。

gc执行过程

  图是运行后的gc日志,我们来逐步分析一下。

1. CommandLine flags:这个是看程序运行时的jvm参数的。有我们命令指定的,也有jvm自身默认的一些参数。
2. GC (Allocation Failure):这个从字面意思上就说了,引发这次gc的原因是"对象分配失败"
2.1 0.206 表示是系统大概运行200多毫秒之后发生的本次gc
2.2 ParNew: 3498K->281K(4608K)表示新生代触发了young gc,用的parnew垃圾回收器。
  3498K   表示gc之前,新生代已经占用的大小,3m*1024=3072k,多的那些除了对象本身大小,还会有对象的一些其他信息
  281K     表示gc之后,存活下来的对象大小,此时它们的年龄就是一岁
  4608K   代表了可用新生代的大小。eden + 一个s区 = 1024*4+512 = 4608k。因为两个s区只有一个是可以放存活对象那个的,另外一个必须是空间的,让新生代的使用率达到90%
2.3 0.0007884 secs 表示此次gc回收这3m对象一共花了0.78毫秒
2.4 3498K->281K(9728K), 0.0008658 secs 是整个堆的内存情况,9728K是整个堆的可用大小,新生代4.5m+老年代5m=9728k
2.5 Times: user=0.00 sys=0.00, real=0.00 secs 表示本次gc消耗时间,因为只有几毫秒,所以以秒为单位来看都是0
3. 下面则是GC过后的堆内存使用情况
  3.1 par new generation total 4608K, used 2411K 表示parnew负责的年轻代总共有4.5m,已使用2.3m(是因为我们后面创建了一个2m的数组)
  3.2 eden space 4096K, 52% used 表示eden区的4m用了一半,存放了我们那个2m的数组
  3.3 from space 512K, 55% used 表示from区占用了一半,就是回收后存活的281k对象
  3.4 to space 512K, 0% used 此时的to区域还没有对象占用
  3.5 concurrent mark-sweep generation total 5120K, used 0K 表示cms管理的5m老年代内存
  3.6 Metaspace used 2671K, capacity 4486K, committed 4864K, reserved 1056768K
     class space used 288K, capacity 386K, committed 512K, reserved 1048576K
     表示的元空间和和class信息

模拟对象进入老年代

/**
 * 案例代码
 */
public class Demo1 {

    public static void main(String[] args) {
        byte[] arry1 = new byte[2 * 1024 * 1024];
        arry1 = new byte[2 * 1024 * 1024];
        arry1 = new byte[2 * 1024 * 1024];
        arry1 = null;

        byte[] arry2 = new byte[256 * 1024];

        /* 1. eden区总共才8m,已占用 6.25 ,此时第一次触发young gc
              arry2就被回收到from区域了、年龄为1;arry3在gc后放入了eden区  */
        byte[] arry3 = new byte[2 * 1024 * 1024];

        arry3 = new byte[2 * 1024 * 1024];
        arry3 = new byte[2 * 1024 * 1024];
        arry3 = new byte[256 * 1024];
        arry3 = null;
        /* 2. 第二次触发young gc,且由于对象>50%from区域,触发了空间担保机制;
              现在 from区域全部进入了老年代(不触发的话还是在from区),arry4进去了eden区  */
        byte[] arry4 = new byte[2 * 1024 * 1024];
    }

}




/**
 * 执行脚本
* 新生代大小设置为10m,最大堆20m,eden和from、to比例为8:1:1 ;对象超过15岁进入老年代;大对象阈值为10m(超过阈值直接进入老年代);垃圾回收器用的 parnew + cms
*/ java -jar -Xmn10M -Xms20M -Xmx20M -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=15 -XX:PretenureSizeThreshold=10M -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:gc.log demo-1.0.jar

Full GC

案例代码

/**
 * 案例代码
 */
public class Demo1 {

    public static void main(String[] args) {
        // 大于阈值3M,直接进入老年代
        byte[] arry1 = new byte[4 *1024 * 1024];
        // 没有gc root引用,成为垃圾
        arry1 = null;
        // eden区创建对象
        byte[] arry2 = new byte[2 *1024 * 1024];
        byte[] arry3 = new byte[2 *1024 * 1024];
        byte[] arry4 = new byte[2 *1024 * 1024];
        byte[] arry5 = new byte[256 * 1024];
        /* 此时eden放不下,触发young gc;
           但是arry2 arry3 arry4 arry5 都被引用在,不会被回收,于是放入老代
           老年代还剩 10-4=6m ,于是老年代放不下触发了full gc
           此时 arr2 arr3 arr4 arr5 都到了老年代。 arr6 在eden区*/
        byte[] arry6 = new byte[2 *1024 * 1024];
    }

}

/**
 * 执行脚本
 * 新生代大小设置为10m,最大堆20m,eden和from、to比例为8:1:1 ;对象超过15岁进入老年代;大对象阈值为3m(超过阈值直接进入老年代);垃圾回收器用的 parnew + cms
 */
java  -jar -Xmn10M -Xms20M -Xmx20M -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=15 -XX:PretenureSizeThreshold=3M -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:gc.log  demo-1.0.jar

GC日志分析

1. byte[] arry6 = new byte[2 *1024 * 1024] 执行这行代码的时候,新生代已经有了6.25m,此时eden区域不够触发了young gc
  ParNew (promotion failed): 6907K->7445K(9216K)
  说明新生代已经用了6907kb空间,在进行gc时发现都在引用无法被回收,所以回收后还剩7445k,from区放不下,则放入老年代
2. CMS: 8194K->6677K(10240K)
  由于之前arr1在老年代占用了4m还剩6m,放不下7445k,所以进行了full gc 回收后 (4m+7445k)回收后还剩 6677k
  11003K->6677K(19456K)
  表示新生代+老年代一共19456k内存,共占用了11003k,回收后还剩6677k
3. [Metaspace: 2664K->2664K(1056768K)] 老年代gc触发时,都会顺手去回收元空间
4. gc完成,arr6在eden区域创建

posted @ 2021-02-04 17:52  吴磊的  阅读(1815)  评论(0编辑  收藏  举报
//生成目录索引列表