场景

新项目上线,为了观察项目运行情况,所以去监控看板去观察。好家伙,一看运行才一会儿就好几次Full GC,吓住我了。如果没有看板,可以通过命令行看:jstat -gccapacity pid。情况如下图:

gc的影响 java java频繁gc原因_java

当我准备+HeapDumpBeforeFullGC去Dump快照下来分析的时候,仔细观察发现,事情好像比较简单,因为Full GC都发生在项目启动的时候。

问题排查

项目启动的时候触发Full GC,说明在项目启动的时候,老年代或者meta区可能频繁扩容导致的(JDK8+)。原因可能是:1、项目启动时加载了比较多或者比较大的对象,导致空间不够,频繁扩容;2、初始化空间太小,不够用,所以进行频繁扩容。一看内存使用情况,很充足,那应该就是后者了。

命令一、jhsdb jmap --heap --pid pid  (查看堆信息)

gc的影响 java java频繁gc原因_加载_02

 可以看到初始化meta空间只有20M多一点的大小(默认的)。

命令二、 jstat -gc pid (查看内存统计信息)

gc的影响 java java频繁gc原因_java_03

 可以看到实际的MetaspaceSize是195+M,已使用也有187+M。

原因

原因就基本知道了,由于初始时没有设置MetaspaceSize的大小,然后项目实际使用的Size接近200M,所以项目初始化的时候就进行Meta区的扩容,触发了Full GC。

解决方法

调整MetaspaceSize,JVM参数添加:

-XX:MetaspaceSize=200m

gc的影响 java java频繁gc原因_加载_04

调整后的效果,Full GC就归零了:

命令三、jstat -gccapacity pid (查看GC统计信息)

gc的影响 java java频繁gc原因_初始化_05

总结

产生Full GC的原因一般就是老年代和Meta区的空间不够用了,至于为什么导致的?还得根据实际情况分析,借助一些分析工具(JVisualVM、Arthas)可以更好地定位问题。其实启动过程的Full GC也系统的运行也没什么大的影响,只是突然出现容易吓人。