背景:组内应用都配置了告警的项目,其中JVM的FullGC配置的是10s内连续FullGC两次即触发告警,近期观察到其中一个java web应用频发告警,打开监控平台后发现每天触发FullGC上千次,由于FullGC会导致STW,造成短时间内应用不可用,一定程度上会影响到用户的使用体验,因此决定排查一下频繁GC的原因。
排查过程:
1.首先查看JVM启动的参数配置,-Xms和-Xmx都是4G,垃圾收集器采用的是PS Scanvage和PS Marksweep,应用的总内存为8G。
从最初主机的运行参数可以简要排查出一些原因,由于FullGC如此频繁,因此原因可能有多个,几个常见触发FullGC的原因有:
(1)手动调用System.gc()
(2)堆内存空间不足
(3)永久代或元空间内存不足
(4)内存担保机制
观察主机其中一段时间内的运行情况,可以看出某些时间堆内存不足是其中的原因之一:
2.除了最简单从运行参数就能看出来的原因,其他原因就需要辅助其他手段,首先增加JVM参数:
-XX:+PrintGCDetails,该参数可以在GC时把相关的信息打印在控制台,根据日志可以进一步分析GC的原因与导致GC背后的代码逻辑。
加上该参数后,由于GC十分频繁,实时日志中的GC也不难找出,GC日志如下:
YoungGC:
YoungGC的原因为Allocation Failure,即内存分配失败,新生代内存空间已满导致的GC,其中箭头左右为GC前后的内存,前一个箭头代表年轻代的内存,后一个箭头代表总堆内存,可以看到发生YoungGC时年轻代的内存基本全被回收,而YoungGC时老年代中只有几百M的对象,相对比较空闲,考虑到当前年轻代与老年代的比例是1:2,即年轻代约1.3G,老年代约2.7G,因此可以适当调整一下分代的比例。FullGC:
FullGC的原因非常令人不可思议。竟然是代码中手动触发的System.gc(),但是全局搜索代码后发现并没有发现有显式调用该方法的地方,因此可能是调用的三方依赖中调用了该方法,具体调用的位置还有待排查,由于基本所有的FullGC都没有与内存有直接关系,只是因为代码中的显式调用,因此先添加参数-XX:-DisableExplicitGC来禁用System.gc(),但JVM的GC仍然有效。