CMS之promotion failed&concurrent mode failure

CMS并行GC收集器是大多数JAVA服务应用的最佳选择,然而, CMS并不是完美的,在使用CMS的过程中会产生2个最让人头痛的问题:

promotion failed

该问题是在进行Minor GC时,Survivor Space放不下,对象只能放入老年代,而此时老年代也放不下造成的。(promotion failed时老年代CMS还没有机会进行回收,又放不下转移到老年代的对象,因此会出现下一个问题concurrent mode failure,需要stop-the-wold 降级为GC-Serail Old)。
下面是一个promotion failed的一条gc日志:

106.641: [GC 106.641: [ParNew (promotion failed): 14784K->14784K(14784K), 0.0370328 secs]106.678: [CMS106.715: [CMS-concurrent-mark: 0.065/0.103 secs] [Times: user=0.17 sys=0.00, real=0.11 secs]
(concurrent mode failure): 41568K->27787K(49152K), 0.2128504 secs] 52402K->27787K(63936K), [CMS Perm : 2086K->2086K(12288K)], 0.2499776 secs] [Times: user=0.28 sys=0.00, real=0.25 secs]

concurrent mode failure

该问题是在执行CMS GC的过程中同时业务线程将对象放入老年代,而此时老年代空间不足,或者在做Minor GC的时候,新生代Survivor空间放不下,需要放入老年代,而老年代也放不下而产生的。
下面是一个concurrent mode failure的一条gc日志:

0.195: [GC 0.195: [ParNew: 2986K->2986K(8128K), 0.0000083 secs]0.195: [CMS0.212: [CMS-concurrent-preclean: 0.011/0.031 secs] [Times: user=0.03 sys=0.02, real=0.03 secs]
(concurrent mode failure): 56046K->138K(57344K), 0.0271519 secs] 59032K->138K(65472K), [CMS Perm : 2079K->2078K(12288K)], 0.0273119 secs] [Times: user=0.03 sys=0.00, real=0.03 secs]

下面我们详细的分析这两个问题产生的原因以及如何进行解决。

首先我们经常遇到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的时候都进行内存的整理。

另外,有些应用存在比较大的对象朝生熄灭,这些对象在救助空间无法容纳,因此,会提早进入老年代,老年代如果有碎片,也会产生promotion failed, 因此我们应该控制这样的对象在新生代,然后在下次Minor GC的时候就被回收掉,这样避免了过早的进行CMS Full GC操作,下面的一个配置样例就通过增加Survivor空间的大小来解决这个问题:

-Xmx4000M -Xms4000M -Xmn600M -XXmSize=500M -XX:MaxPermSize=500M -Xss256K -XX:+DisableExplicitGC -XX:SurvivorRatio=1 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled eCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=0 -XX:+CMSClassUnloadingEnabled -XX:LargePageSizeInBytes=128M -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=80 -XX:SoftRefLRUPolicyMSPerMB=0 -XX:+PrintClassHistogram -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintHeapAtGC -Xloggc:log/gc.log

上面讨论了promotion failed引起的原因以及解决方案,除了promotion failed还有一个情况会引起CMS回收失败,从而退回到Serial Old收集器进行回收,我们在线上尤其要注意的是concurrent mode failure出现的频率,这可以通过-XX:+PrintGCDetails来观察,当出现concurrent mode failure的现象时,就意味着此时JVM将继续采用Stop-The-World的方式来进行Full GC,这种情况下,CMS就没什么意义了,造成concurrent mode failure的原因是当minor GC进行时,1)旧生代所剩下的空间小于Eden区域+From区域的空间,或者2)在CMS执行老年代的回收时有业务线程试图将大的对象放入老年代,导致CMS在老年代的回收慢于业务对象对老年代内存的分配。

解决这个问题的通用方法是调低触发CMS GC执行的阀值,CMS GC触发主要由CMSInitiatingOccupancyFraction值决定,默认情况是当旧生代已用空间为68%时,即触发CMS GC,在出现concurrent mode failure的情况下,可考虑调小这个值,提前CMS GC的触发,以保证旧生代有足够的空间。

总结:

  1. promotion failed – concurrent mode failure
    Minor GC后, Survivor空间容纳不了剩余对象,将要放入老年代,老年代有碎片或者不能容纳这些对象,就产生了concurrent mode failure, 然后进行stop-the-world的Serial Old收集器。

解决办法:-XX:UseCMSCompactAtFullCollection -XX:CMSFullGCBeforeCompaction=5 或者调大新生代或者Survivor空间

  1. concurrent mode failure

CMS是和业务线程并发运行的,在执行CMS的过程中有业务对象需要在老年代直接分配,例如大对象,但是老年代没有足够的空间来分配,所以导致concurrent mode failure, 然后需要进行stop-the-world的Serial Old收集器。

解决办法:+XX:CMSInitiatingOccupancyFraction,调大老年带的空间,+XX:CMSMaxAbortablePrecleanTime

总结一句话:使用标记整理清除碎片和提早进行CMS操作。

转载于:https://www.cnblogs.com/z-test/p/10197271.html

### Java 中的垃圾回收器种类 #### 并行垃圾回收器 (Parallel GC) 并行垃圾回收器旨在最小化吞吐量时间,适用于多处理器或多线程硬件环境下的应用程序。此收集器采用一种称为“停止世界”的方式,在进行垃圾回收期间会暂停所有的应用程序线程。它主要针对年轻代使用复制算法,而对于年老代则采用了标记-清除或标记-压缩算法[^1]。 #### CMS 垃圾回收器 (Concurrent Mark-Sweep, CMS) CMS 收集器是一种低延迟优先级的垃圾回收器,目标是在最短时间内完成垃圾回收过程以减少停顿时间。该收集器会在后台并发地执行大部分工作,只在某些特定阶段短暂地暂停应用线程。然而,由于其设计特点,可能会存在浮动垃圾问题,并且当堆内存不足时可能导致promotion failedconcurrent mode failure等问题[^2]。 #### G1 垃圾回收器 (Garbage First, G1) G1 是一款分区式的、基于区域(Region)模型工作的新型垃圾收集器。相比于之前的串行/并行GC以及CMS而言,G1不仅能够实现更短的停顿时间和更好的可预测性,还支持大容量堆的有效管理。G1 将整个堆划分为多个大小相等的小块(Region),并通过预测性的分步清扫来达到高效的垃圾处理效果[^3]。 #### ZGC 垃圾回收器 ZGC是一款超低延时的垃圾回收器,能够在数毫秒内完成全局指针更新操作而不影响正常的应用程序运行。这使得即使面对非常大的堆尺寸也能保持极高的响应速度和服务质量。ZGC引入了染色摘要(Coloring Summary)技术以及其他创新机制,从而实现了几乎无停顿的目标[^4]。 #### Shenandoah 垃圾回收器 Shenandoah也是一种致力于降低停顿时间的新一代垃圾收集器。它的核心理念是通过与正在运行的应用程序同时进行疏散(Evacuation)的方式来进行垃圾回收活动,这样就可以极大地缩短完全停止的时间窗口。此外,Shenandoah同样具备良好的扩展性和适应大规模数据的能力[^5]。 ```java // 示例代码展示如何设置不同的垃圾回收器参数 public class GarbageCollectorExample { public static void main(String[] args) { System.setProperty("java.vm.option", "-XX:+UseG1GC"); // 设置为G1垃圾回收器 // 或者其他选项如下所示: // -XX:+UseSerialGC // 单线程版本 // -XX:+UseParallelGC // 多线程版本 // -XX:+UseConcMarkSweepGC // CMS垃圾回收器 // -XX:+UseZGC // ZGC垃圾回收器 // -XX:+UseShenandoahGC // Shenandoah垃圾回收器 } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值