JavaOldGCAlertV2/JavaCMSOldGCAlertV2
背景知识 (background)
FullGC通常伴随着比较久的停顿和性能退化,不过不同GC算法关注点不一样。
对CMS来说,FullGC是比较正常的,每次STW也比较短,但频繁的话会导致吞吐量下降,因此重点考察CMS FullGC的频率,目前1分钟超过12次就报警。
CMS分两种模式:Background和Foreground,严格来说,Foreground才算FullGC。Foreground在Promotion Failed、Concurrent Mode Failure等条件下触发,是单线程串行回收的,对整个老年代进行清理、压缩,消除内存碎片,STW 时间长,有时会长达十几秒。Background是正常情况下触发的并发回收的 CMS GC,停顿非常短,对业务影响很小。
promotion failed
该问题是在进行Minor GC时,Survivor Space放不下,对象只能放入老年代,而此时老年代也放不下造成的。(promotion failed时老年代CMS还没有机会进行回收,又放不下转移到老年代的对象,因此会出现下一个问题concurrent mode failure,需要stop-the-wold 降级为GC-Serail Old)。
concurrent mode failure
该问题是在执行CMS GC的过程中同时业务线程将对象放入老年代,而此时老年代空间不足,或者在做Minor GC的时候,新生代Survivor空间放不下,需要放入老年代,而老年代也放不下而产生的。
首先promotion failed问题,一般是进行Minor GC的时候,发现Survivor空间不够,所以,需要移动一些新生带的对象到老年代,然而有些时候尽管老年代有足够的空间,但是由于CMS采用标记清除算法,默认并不使用标记整理算法,可能会产生很多碎片,因此,这些碎片无法完成大对象向老年带转移,因此需要进行CMS在老年带的Full GC来合并碎片。
这个问题的直接影响就是它会导致提前进行CMS Full GC, 尽管这个时候CMS的老年代并没有填满,只不过有过多的碎片而已,但是Full GC导致的stop-the-wold是难以接受的。
解决这个问题的办法就是可以让CMS在进行一定次数的Full GC(标记清除)的时候进行一次标记整理算法,CMS提供了以下参数来控制:
-XX:UseCMSCompactAtFullCollection -XX:CMSFullGCBeforeCompaction=5
也就是CMS在进行5次Full GC(标记清除)之后进行一次标记整理算法,从而可以控制老年代的碎片在一定的数量以内,甚至可以配置CMS在每次Full GC的时候都进行内存的整理。
这两个参数UseCMSCompactAtFullCollection默认true、CMSFullGCsBeforeCompaction默认0,在UseCMSCompactAtFullCollection==true时以下三种情况会进行空间压缩:
第一种是_full_gcs_since_conc_gc>=CMSFullGCsBeforeCompaction
第二种是调用了System.gc()(DisableExplicitGC没有开启)
第三种条件是年轻代预计老年代没有足够空间来容纳下次晋升的对象;上周的gc pause报警,还是大对象的问题
promotion failed – concurrent mode failure
Minor GC后, Survivor空间容纳不了剩余对象,将要放入老年代,老年代有碎片或者不能容纳这些对象,就产生了concurrent mode failure, 然后进行stop-the-world的Serial Old收集器。
对G1来说,FullGC对整个堆(Young+Old Generation)进行清理,通常意味着更大的停顿时长,是我们要竭力避免的,因此出现一次就报警。
止损措施 (action)
FullGC发生时,已产生负面影响。如果集中在单个节点,可考虑摘流或者调小节点权重。
建议关注Old区的增长,在下次FullGC前,先手动摘流,然后Dump堆内存。