Concurrent Mode Failure 是 CMS 垃圾收集器因老年代内存压力与并发清理节奏脱节导致的退化现象,触发 Serial Old Full GC 并引发明显 STW;需通过提前启动 CMS、减少晋升、控制浮动垃圾来解决。

Concurrent Mode Failure 是 CMS 垃圾收集器特有的退化现象,不是配置写错了,而是老年代的内存压力节奏和 CMS 的并发清理节奏脱节了。它会强制触发 Serial Old 类型的 Full GC,带来明显 STW 停顿,业务常表现为接口超时、线程堆积或 500 错误。解决关键不在堆大小,而在让 CMS “来得及干活”。
看懂日志里的失败信号
先确认是否真发生了 Concurrent Mode Failure:
- GC 日志中明确出现 concurrent mode failure(大小写不敏感)字样
- 紧随其后是 [Full GC (Allocation Failure)],而非正常的 CMS concurrent cycle 完整阶段(如 missing cleanup 或 abortable-preclean 后直接跳 Full GC)
- 对比前后老年代使用量(OU),发现 CMS 并发周期内占用率快速攀升,例如从 65% → 92% → 触发失败
- 同时 ParNew 晋升量大,或日志中夹杂 promotion failed,说明 Survivor 区已溢出,大量对象被迫直奔老年代
核心调优方向:早启动 + 少晋升 + 控浮动垃圾
CMS 预留空间本质是“安全垫”,垫不住就失败。重点调整三方面:
- 提前启动 CMS:显式设置 -XX:CMSInitiatingOccupancyFraction=50(建议 45–60),并加 -XX:+UseCMSInitiatingOccupancyOnly 禁用动态阈值,避免 JDK7+ 默认 75%+ 的高危起点
- 减少老年代“突发涌入”:增大年轻代(-Xmn)或调大 SurvivorRatio(如 -XX:SurvivorRatio=8),缓解 Survivor 溢出导致的过早晋升;检查是否存在大对象(> Eden/2)直接分配进老年代
- 压住浮动垃圾:避免在 CMS 并发周期(尤其是 mark 和 preclean 阶段)内密集打 DEBUG 日志、无界缓存 put、JSON 反序列化生成大量临时对象——这些对象生命周期长,又不被本轮 CMS 标记,白白吃掉预留空间
辅助手段与长期演进
有些问题单靠 CMS 参数难根治,需配合监控与架构升级:
立即学习“Java免费学习笔记(深入)”;
- 用 jstat -gc <pid> 1000 实时观察 OU(老年代使用量)、YGC 频率和每次晋升量,确认晋升是否稳定;OU 持续 >70% 且波动剧烈,就是预警信号
- 开启 -XX:+PrintGCDetails -XX:+PrintGCTimeStamps,结合 GC 日志时间戳定位失败是否集中在某个定时任务或流量高峰时段
- 碎片严重(如频繁出现 to-space exhausted 或 evacuation failure)时,可临时启用 -XX:+UseCMSCompactAtFullCollection,但注意它只对 Full GC 有效,不能预防 Concurrent Mode Failure
- 长期建议迁移到 G1 或 ZGC:CMS 已在 JDK9 标记为废弃,JDK14 彻底移除;G1 自带可预测停顿和自动碎片整理,ZGC 更支持 TB 级堆与亚毫秒级 STW,更适合现代服务场景


















