并发失败(concurrent mode failure)是cms在并发标记/清理阶段因老年代预留空间被耗尽而退化为full gc的问题,主因是晋升速率突增、浮动垃圾过多及触发阈值过高;解决需调低-xx:cmsinitiatingoccupancyfraction、优化晋升行为并监控ou/ygc,长期应迁移到g1/zgc。

并发失败(Concurrent Mode Failure)是 CMS 垃圾收集器在并发清理阶段因老年代空间不足而被迫退化为 Full GC 的典型问题,本质是 CMS 无法在并发标记/清理过程中及时回收足够空间,导致“预留空间”被耗尽。它不是配置错误,而是动态负载与内存分配节奏不匹配的信号。
为什么 CMS 会预留空间?
CMS 在并发标记期间,用户线程持续运行并可能向老年代分配对象(如大对象直接晋升、年轻代 Survivor 区过小导致提前晋升),因此必须预留一部分空间作为“安全垫”,防止并发阶段结束时老年代已满。这个预留量由 JVM 自动估算,主要受以下因素影响:
- -XX:CMSInitiatingOccupancyFraction:设定老年代使用率达到多少时启动 CMS 初始标记(默认约 68%,JDK7 后动态调整);值设得太低会频繁触发 CMS,太高则易引发并发失败
- 浮动垃圾(Floating Garbage):并发标记开始后新产生的、尚未被标记为可回收的对象,它们占用的空间必须被预留出来
- 晋升速率不稳定:突发流量导致年轻代对象大量、快速晋升至老年代,超出 CMS 清理速度
如何确认发生了 Concurrent Mode Failure?
查看 GC 日志中是否出现以下关键标识:
- concurrent mode failure 或 Concurrent Mode Failure(大小写可能不同)
- 紧随其后出现 Full GC 或 GC pause (full),且 Cause 为 Allocation Failure 或 CMF
- 日志中显示 CMS concurrent cycle 被中断,例如:
[CMS-concurrent-abortable-preclean: 2.123/3.456 secs]后无 cleanup 阶段,直接跳转 Full GC
核心解决策略:稳住晋升节奏 + 留足缓冲空间
重点不在“加大老年代”,而在让 CMS 的并发节奏跟上实际内存压力:
-
调低触发阈值:显式设置
-XX:CMSInitiatingOccupancyFraction=50(建议 45–60 区间),让 CMS 更早介入,避免临界点抢跑失败 -
控制晋升行为:增大年轻代(
-Xmn)或调整 SurvivorRatio(-XX:SurvivorRatio),减少因 Survivor 溢出导致的过早晋升;启用-XX:+UseCMSCompactAtFullCollection减少碎片(仅对 Full GC 有效) - 抑制浮动垃圾增长:避免在 CMS 并发周期内创建大量长生命周期临时对象;检查是否有缓存未设上限、日志输出过于密集等隐式内存压力源
-
监控辅助决策:开启
-XX:+PrintGCDetails -XX:+PrintGCTimeStamps,配合jstat -gc <pid></pid>观察OC(老年代容量)、OU(已用)、YGC晋升量(EU→OU变化差值)
替代方案:何时该放弃 CMS?
CMS 已在 JDK9 中被标记为废弃,JDK14 彻底移除。若系统仍运行在 JDK8 且频繁遭遇 CMF,说明业务模型已超出 CMS 设计边界:
- 高吞吐、低延迟要求稳定 → 迁移至 G1,配置
-XX:+UseG1GC -XX:MaxGCPauseMillis=200 - 超大堆(>32GB)、追求确定性停顿 → 考虑 ZGC(JDK11+)或 Shenandoah(JDK12+)
- 升级前过渡期,可用
-XX:+UseParNewGC -XX:+UseConcMarkSweepGC组合 + 上述调优项争取稳定窗口











