其中,「FGC过于频繁」和「YGC耗时过长」,这两种情况属于比较典型的GC问题,大概率会对程序的服务质量产生影响。剩余两种情况的严重程度低一些,但是对于高并发或者高可用的程序也需要关注。
03 排查FGC问题的实践指南
通过上面的案例分析以及理论介绍,再总结下FGC问题的排查思路,作为一份实践指南供大家参考。
1. 清楚从程序角度,有哪些原因导致FGC?
1、大对象:系统一次性加载了过多数据到内存中(比如SQL查询未做分页),导致大对象进入了老年代。
2、内存泄漏:频繁创建了大量对象,但是无法被回收(比如IO对象使用完后未调用close方法释放资源),先引发FGC,最后导致OOM.
3、程序频繁生成一些长生命周期的对象,当这些对象的存活年龄超过分代年龄时便会进入老年代,最后引发FGC. (即本文中的案例)
4、程序BUG导致动态生成了很多新类,使得 Metaspace 不断被占用,先引发FGC,最后导致OOM.
5、代码中显式调用了gc方法,包括自己的代码甚至框架中的代码。
6、JVM参数设置问题:包括总内存大小、新生代和老年代的大小、Eden区和S区的大小、元空间大小、垃圾回收算法等等。
2. 清楚排查问题时能使用哪些工具
1、公司的监控系统:大部分公司都会有,可全方位监控JVM的各项指标。
2、JDK的自带工具,包括jmap、jstat等常用命令:
查看堆内存各区域的使用率以及GC情况
jstat -gcutil -h20 pid 1000
查看堆内存中的存活对象,并按空间排序
jmap -histo pid | head -n20
dump堆内存文件
jmap -dump:format=b,file=heap pid
3、可视化的堆内存分析工具:JVisualVM、MAT等
3. 排查指南
1、查看监控,以了解出现问题的时间点以及当前FGC的频率(可对比正常情况看频率是否正常)
2、了解该时间点之前有没有程序上线、基础组件升级等情况。
3、了解JVM的参数设置,包括:堆空间各个区域的大小设置,新生代和老年代分别采用了哪些垃圾收集器,然后分析JVM参数设置是否合理。
4、再对步骤1中列出的可能原因做排除法,其中元空间被打满、内存泄漏、代码显式调用gc方法比较容易排查。
5、针对大对象或者长生命周期对象导致的FGC,可通过 jmap -histo 命令并结合dump堆内存文件作进一步分析,需要先定位到可疑对象。
6、通过可疑对象定位到具体代码再次分析,这时候要结合GC原理和JVM参数设置,弄清楚可疑对象是否满足了进入到老年代的条件才能下结论。
04 最后的话
这篇文章通过线上案例并结合GC原理详细介绍了FGC的排查过程,同时给出了一份实践指南。
后续会以类似的方式,再分享一个YGC耗时过长的案例,希望能帮助大家吃透GC问题排查,如果觉得本文对你有帮助,请大家关注我的个人公众号!
- End -
作者简介:程序员,985硕士,前亚马逊Java工程师,现58转转技术总监。持续分享技术和管理方向的文章。如果感兴趣,可微信扫描下面的二维码关注我的公众号:『IT人的职场进阶』