如何分析GC的日志

171 阅读3分钟

1. 背景

当你遇到线上服务内存偏高,偶然激增,你需要去heap dump分析内存,结合GC日志分析内存的释放情况

2. GC日志分析

2.1 基本结构

image.png

2.2 日志分析

[GC (Allocation Failure) [ParNew: 367523K->1293K(410432K), 0.0023988 secs] 522739K->156516K(1322496K), 0.0025301 secs] [Times: user=0.04 sys=0.00, real=0.01 secs]

GC:表明进行了一次垃圾回收,前面没有Full修饰,表明这是一次Minor GC ,注意它不表示只GC新生代,并且现有的不管是新生代还是老年代都会STW。

Allocation Failure:表明本次引起GC的原因是因为在年轻代中没有足够的空间能够存储新的数据了。

ParNew:表明本次GC发生在年轻代并且使用的是ParNew垃圾收集器。ParNew是一个Serial收集器的多线程版本,会使用多个CPU和线程完成垃圾收集工作(默认使用的线程数和CPU数相同,可以使用-XX:ParallelGCThreads参数限制)。该收集器采用复制算法回收内存,期间会停止其他工作线程,即Stop The World。

367523K->1293K(410432K):单位是KB, 三个参数分别为:GC前该内存区域(这里是年轻代)使用容量,GC后该内存区域使用容量,该内存区域总容量。

0.0023988 secs:该内存区域GC耗时,单位是秒

522739K->156516K(1322496K):三个参数分别为:堆区垃圾回收前的大小,堆区垃圾回收后的大小,堆区总大小。

0.0025301 secs:该内存区域GC耗时,单位是秒[Times: user=0.04 sys=0.00, real=0.01 secs]:分别表示用户态耗时,内核态耗时和总耗时

分析下可以得出结论:该次GC新生代减少了367523-1293=366239K, Heap区总共减少了522739-156516=366223K, 366239 – 366223 =16K,说明该次共有16K内存从年轻代移到了老年代,可以看出来数量并不多,说明都是生命周期短的对象,只是这种对象有很多。

我们需要的是尽量避免Full GC的发生,让对象尽可能的在年轻代就回收掉,所以这里可以稍微增加一点年轻代的大小,让那17K的数据也保存在年轻代中。

4. GC日志的error分析

ParNew + CMS 组合, 在 CMS 回收器触发时,出现了 promotion failed 和 concurrent mode failure 现象:

promotion failed, 该现象是在进行触发年轻代 ParNew GC 时,存活的对象在 Survivor 区放不下,对象只能进入老年代,而此时老年代也放不下导致的。

concurrent mode failure, 该现象是在执行 CMS 回收器回收垃圾的过程中同时有存活的对象放入老年代,而此时老年代空间不足,或者在做 ParNew GC 的时候,年轻代 Survivor 区放不下,需要放入老年代,而老年代也放不下而导致的。

针对以上2种现象产生的原因进行 JVM 相关参数优化:

可增大年轻代或者 Survivor 区的存储空间, -Xmn1500M, -XX:SurvivorRatio=8

或者提前触发 CMS 垃圾回收和进行 5 次 CMS 垃圾回收后整理清除碎片

-XX:+UseCMSCompactAtFullCollection

-XX:CMSFullGCsBeforeCompaction=5

-XX:+UseCMSInitiatingOccupancyOnly

-XX:CMSInitiatingOccupancyFraction=80

对于生产系统,合理增大年轻代内存大小,本着尽量减少系统 Minor GC,一日最多一次 Full GC的原则

3. 参考资料

blog.csdn.net/zc19921215/…

cloud.tencent.com/developer/i…