现象
通过监控系统平台告警感知到,某应用某IP在某段时间内出现FullGC,FullGC的过程伴随着STW,如果过于频繁,将会导致用户体验极具下降。FullGC的出现经常伴随老年代空间被打满的情况,可以通过监控视图来观测到,当前统一使用CMS垃圾收集器作为老年代GC通用收集方式,以下参数针对CMS有效。
JVM参数设置不合理
老年代空间设置太小
分析路径
通过MDC监控JVM视图可以观察到老年代空间是否打满,再结合系统配置的-Xmx(堆内存最大大小年) -Xmn(堆内存新生代)基本可以判断是否老年代空间大小配置的合理性。
解决方案
调大-Xmx到一个合理范围,降低老年代空间被占满的概率,从而降低fullGC发生的频次。
年轻代空间设置太小
- 如果同时指定了NewRatio和NewSize,新生代空间大小 = min(MaxNewSize, max(NewSize, heap/(NewRatio+1)))。
- 如果只设置了NewRation,新生代大小空间 = 1/(NewRatio +1)。
- 如果指定了NewSize和MaxNewSize,那新生代大小空间就是指定的NewSize,然后可以一直增长到MaxNewSize。
- 如果年轻代完全不配置则会按照默认情况计算:机器硬件决定(x86位64M)*并行线程数(-XX:ParallelGCThreads指定的值)*13 / 10
分析路径
年轻代空间设置太小,会导致新对象无法容纳,从而使得新生代的对象更加容易被转移到老年代中,从而导致老年代空间使用率过高触发FullGC。
解决方案
通过–XX:NewRatio或–XX:NewSize、–XX:MaxNewSize调整新生代空间在堆空间的占比。
元空间设置太小
- -XX:MetaspaceSize:初始空间大小,达到该值就会触发垃圾收集进行类型卸载,同时GC会对该值进行调整:如果释放了大量的空间,就适当降低该值;如果释放了很少的空间,那么在不超过MaxMetaspaceSize时,适当提高该值。
- -XX:MaxMetaspaceSize:最大空间,默认是没有限制的。 也就是说你的系统内存上限是多少它就是多少
分析路径
JDk8通过本地内存来存放类、常量、静态变量等元数据信息。当系统类数量多、字节码动态生成类信息多超过-XX:MetaspaceSize大小时会触发FullGC。
解决方案
提升元空间大小。
业务代码不合理
分析路径
- 代码中创建的大对象需超过-XX:PretenureSizeThreshold阈值时会直接进入老年代,当老年代空间无法容纳新晋对象时就会触发FullGC。
- 当晋升到老年代的是长度较长的数组时,此时老年代空间可能并没有打满,但因为无法找到连续空间分配大数组,也会触发FullGC。
- 内存泄漏(如没有释放IO资源、ThreadLoca没有调用remove清理数据等)都会导致堆空间打满,提升FullGc发生的概率。
排查方式
可以通过jmap下载heapdump文件分析,分析工具可以借助Jprofiler或者visualVM UI工具直观定位到代码中大对象的位置。也可以通过jstack 和jstat查看线程GC情况。
解决方案
修改业务代码,避免创建大的字符串或者数组,降低对象由于太大超过阈值直接进入老年代触发fullGC的概率。