CMS发生Concurrent Mode Failure表明其清理节奏跟不上内存分配,需调低启动阈值(如-XX:CMSInitiatingOccupancyFraction=50)、稳住晋升节奏、抑制浮动垃圾,并尽快迁移到G1/ZGC等现代收集器。

遇到 Concurrent Mode Failure,说明 CMS 已经来不及清理老年代,被迫退化为 Serial Old 执行 Full GC,停顿明显、业务受损。这不是偶然配置失误,而是内存分配节奏和 CMS 清理节奏脱节的明确信号。
调低 CMS 启动阈值,给清理留出反应时间
CMS 默认在老年代占用约 68%~92% 时才启动,太晚了。流量一冲,预留空间瞬间被填满。必须主动干预:
- 显式设置 -XX:CMSInitiatingOccupancyFraction=50(建议 45–60 区间)
- 搭配 -XX:+UseCMSInitiatingOccupancyOnly,禁用 JDK7+ 的动态调整机制,避免阈值悄悄升高
- 配合 jstat -gc <pid> 观察 OU(老年代使用量)变化趋势,确认是否在设定阈值附近稳定触发 CMS
稳住晋升节奏,减少老年代“突发涌入”
年轻代对象大量、快速晋升是并发失败的直接推手。重点不是压低总分配量,而是控制晋升的稳定性与规模:
- 适当增大年轻代:-Xmn(例如从 512M 提到 1G),延长对象在年轻代存活时间
- 调优 Survivor 区比例:-XX:SurvivorRatio=6 或更高,避免 Survivor 溢出导致提前晋升
- 检查是否存在大对象直接分配进老年代(如超大 byte[]、Map),考虑拆分或池化
抑制浮动垃圾,降低“隐性空间占用”
并发标记开始后新产生的长生命周期对象,会成为浮动垃圾,持续挤占预留空间。这类压力常被忽视:
立即学习“Java免费学习笔记(深入)”;
- 避免在 CMS 并发周期内密集输出 DEBUG 日志、生成大量 JSON 中间对象
- 审查缓存组件(如 Guava Cache、Caffeine),确认设置了 size limit 或 expireAfterWrite
- 监控 GC 日志中 CMS-concurrent-abortable-preclean 阶段是否耗时异常或频繁失败——这是晋升压力过大的典型征兆
长期来看,该考虑迁移收集器
CMS 在 JDK9 被标记为废弃,JDK14 正式移除。即使当前调优有效,也属临时止血:
- 新项目优先选用 G1:自带压缩、可预测停顿、自动适应负载
- 高吞吐低延迟场景可评估 ZGC 或 Shenandoah:亚毫秒级 STW,真正面向未来
- 若暂无法升级,至少启用 -XX:+UseCMSCompactAtFullCollection 减少碎片,缓解因空间不连续引发的连锁失败


















