频繁 Full GC 怎么办?

Full GC问题的排查思路如下:

1)明确造成Full GC的可能原因:

  • 大对象:系统一次性加载了过多数据到内存中(例如未进行分页的SQL查询),导致大对象进入老年代。
  • 内存泄漏:频繁创建了大量对象,但无法被回收(例如未调用close方法释放资源的IO对象),先引发Full GC,最终导致OOM。
  • 程序频繁生成长生命周期对象:当这些对象的存活年龄超过分代年龄时,它们将进入老年代,最终引发Full GC。
  • 程序中的BUG
  • 代码中显式调用了gc方法,包括自己的代码甚至框架代码。
  • JVM参数设置问题:包括总内存大小、新生代和老年代大小、Eden区和Survivor区大小、元空间大小、垃圾回收算法等。

2)确定可以使用哪些工具进行排查:

  • 公司的监控系统:大多数公司都会有自己的监控系统,可以全面监控JVM的各项指标。
  • JDK自带工具,例如jmap、jstat等常用命令:
# 查看各内存区域的使用率和GC情况
jstat -gcutil -h20 pid 1000
# 查看堆内存中的存活对象,并按空间排序
jmap -histo pid | head -n20
# 导出堆内存快照
jmap -dump:format=b,file=heap pid
  • 可视化堆内存分析工具:JVisualVM、MAT等。

3)进行排查的指南:

  • 检查监控数据,了解出现问题的时间点以及当前Full GC的频率(与正常情况对比)。
  • 了解该时间点之前是否有程序上线、基础组件升级等情况。
  • 理解JVM的参数设置,包括各个内存区域的大小、新生代和老年代使用的垃圾收集器等,并分析JVM参数设置是否合理。
  • 根据步骤1列出的可能原因进行逐一排除,其中元空间溢出、内存泄漏和显式调用gc方法相对较容易排查。
  • 对于由大对象或长生命周期对象引起的Full GC,可以使用jmap -histo命令结合堆内存快照进行进一步分析,需要先定位到可疑对象。
  • 通过可疑对象定位到具体代码,再次进行分析。此时需要结合GC原理和JVM参数设置,确定可疑对象是否满足进入老年代的条件,才能得出结论。

标签: java, Java面试题, Java问题合集, Java编程, Java问题精选, Java常见问题