这篇文章主要为大家展示了“JAVA中GC日志的相关知识有哪些”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“JAVA中GC日志的相关知识有哪些”这篇文章吧。
GC环境模拟
首先我们给出如下代码用来触发GC
public static void main(String[] args) { // 每100毫秒创建100线程,每个线程创建一个1M的对象,即每100ms申请100M堆空间 Executors.newScheduledThreadPool(1).scheduleAtFixedRate(() -> { for (int i = 0; i < 100; i++) { new Thread(() -> { try { // 申请1M byte[] temp = new byte[1024 * 1024]; Thread.sleep(new Random().nextInt(1000)); // 随机睡眠1秒以内 } catch (InterruptedException e) { e.printStackTrace(); } }).start(); } }, 1000, 100, TimeUnit.MILLISECONDS); }
我们要模拟的场景是年轻代不断地Young GC,并有一部分对象晋升到老年代,当老年代空间不足时触发Full GC。
程序逻辑:每100毫秒钟创建100个线程,每个线程创建一个1M的对象,即每100ms申请100M堆空间。之所以每个线程随机睡眠1s,是为了避免对象朝生夕灭,保证可以有一部分对象能晋升到老年代,更好的触发Young GC 和 Full GC,注意这个睡眠时间如果大了,会导致OOM,如果小了,很难触发FULL GC。
虚拟机参数解释
启动Java进程:java -Xms200m -Xmx200m -Xmn100m -verbose:gc -XX:+PrintGCDetails -Xloggc:./gc.log -XX:+PrintGCDateStamps -jar demo-0.0.1-SNAPSHOT.jar
-Xms200m -Xmx200m 最小/最大堆内存 200M
-Xmn100m 年轻代内存 100M
-verbose:gc 开启GC日志
-XX:+PrintGCDetails -Xloggc:./gc.log -XX:+PrintGCDateStamps 将GC日志详情输入到gc.log中
jmap分析
jcmd 获取我们Java进程的Id:6264
jmap -heap 6264查看堆信息
第一次查看,我们发现 Eden区是98M,S0、S1是1M
第二次查看, Eden区是99M,S0、S1是0.5M
Eden区与Survivor区的比例在动态的变化,并不是默认的8:1:1。
原来我们使用默认的垃圾收集器Parallel Scavenge+Parallel Old组合,而该收集器下-XX:+UseAdaptiveSizePolicy是默认开启的,即Eden区与Survivor区比例根据GC情况会自适应变化。
我们加上参数,关闭年轻代自适应,年轻代比例设置为8:1:1
-XX:-UseAdaptiveSizePolicy -XX:SurvivorRatio=8
另外为了尽早的触发FULL GC,我们新增虚拟机参数
-XX:MaxTenuringThreshold=10
晋升年龄由默认的15修改为10,使得年轻代的对象更容易晋升到老年代
重启虚拟机查看jmap
年轻代
Eden区80M 已使用51M,当前使用率63.8%
S0区10M 已使用0.43M,使用率4.37%
S1区10M 使用率为空
老年代
100M 已使用18.39M,使用率18.9%
GC日志内容分析
查看我们输出的GC日志gc.log,选取其中两段
2019-06-09T02:55:30.993+0800: 330.811: [GC (Allocation Failure) [PSYoungGen: 82004K->384K(92160K)] 184303K->102715K(194560K), 0.0035647 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 2019-06-09T02:55:30.997+0800: 330.815: [Full GC (Ergonomics) [PSYoungGen: 384K->0K(92160K)] [ParOldGen: 102331K->5368K(102400K)] 102715K->5368K(194560K), [Metaspace: 16941K->16914K(1064960K)], 0.0213953 secs] [Times: user=0.02 sys=0.00, real=0.02 secs]
Young GC
2019-06-09T02:55:30.993+0800: 330.811: [GC (Allocation Failure) [PSYoungGen: 82004K->384K(92160K)] 184303K->102715K(194560K), 0.0035647 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 2019-06-09T02:55:30.997+0800: 330.815: [Full GC (Ergonomics) [PSYoungGen: 384K->0K(92160K)] [ParOldGen: 102331K->5368K(102400K)] 102715K->5368K(194560K), [Metaspace: 16941K->16914K(1064960K)], 0.0213953 secs] [Times: user=0.02 sys=0.00, real=0.02 secs]
解释:
年轻代GC:[GC前年轻代80.08M->GC后0.37M(年轻代总大小90M)]GC前堆179.98M->GC后堆100.3M(堆总大小190M),用时]
其中年轻代总大小是90M而不是100M,这里我理解是年轻代当前最大申请到90M
100M*80%=80M 是Eden区大小
80M*80% = 64M Eden区默认占用超过8成即64M就会触发YoungGC
Full GC
[Full GC (Ergonomics) [PSYoungGen: 384K->0K(92160K)] [ParOldGen: 102331K->5368K(102400K)] 102715K->5368K(194560K), [Metaspace: 16941K->16914K(1064960K)], 0.0213953 secs] [Times: user=0.02 sys=0.00, real=0.02 secs]
解释:
[GC前年轻代0.375M->GC后年轻代0M(年轻代总大小90M)][GC前老年代99.93M->GC后老年代5.24M(老年代总大小100M)]GC前堆100.3M->GC后堆5.24M(堆总大小190M),[元数据区:GC前16.5,GC后16.5(元数据区总大小1040M)],用时]
可以推测出此次FullGC原因是年轻代晋升老年代空间不足导致
利用可视化工具分析
这里我们利用 gceasy.io/ 分析一下
(1)统计年轻代、老年代、元数据区最大可用空间以及峰值,这里元数据区大小在我们的虚拟机参数没有配置,所以取的是默认值
(2)吞吐量、GC平均延迟、最大延迟以及延迟区间的统计
(3)堆所用大小的实时分析,红色位置是发生了FullGC使得堆总量直线下降
会发现虚拟机在刚启动不久的阶段触发大量的FULL GC,我的理解是我们申请的对象都随机睡眠一秒以内,刚启动时大部分还存在线程的引用,GCRoot可达。在刚启动的时候触发FULL GC并不会完整清理掉老年代空间并由于空间不足不断触发FULL GC。
(4)GC空间总量和时间的统计
(5)各类GC时间、GC次数、GC总量等指标
以上是“JAVA中GC日志的相关知识有哪些”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注亿速云行业资讯频道!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。