cms并发模式失败是因老年代空间压力与cms清理节奏脱节,导致被迫退化为serial old执行full gc;需调低触发阈值(如-xx:cmsinitiatingoccupancyfraction=50)、控制晋升节奏、抑制浮动垃圾,并通过gc日志监控确认问题。

CMS并发模式失败(Concurrent Mode Failure)不是配置写错了,而是老年代空间压力节奏和CMS清理节奏脱节了。它表现为CMS还没清完,老年代就满了,JVM被迫退化为Serial Old执行Full GC,停顿时间陡增,业务明显卡顿甚至超时。
调低CMS触发阈值,让回收更早介入
CMS默认在老年代使用率达70%以上才启动,但流量一波动就容易“抢跑失败”。建议显式设置触发比例,给清理留出缓冲窗口:
- 添加参数:-XX:CMSInitiatingOccupancyFraction=50(推荐45–60区间)
- 搭配-XX:+UseCMSInitiatingOccupancyOnly,禁用JVM动态调整,避免运行中突变
- 配合jstat -gc
持续观察OU(老年代使用量)趋势,确认是否稳定在触发线之下
控制对象晋升节奏,减少老年代突增压力
年轻代GC后大量对象涌进老年代,是压垮预留空间的主因之一。重点稳住晋升行为:
- 适当增大年轻代:-Xmn,延长对象在新生代的存活时间
- 调大Survivor区比例:-XX:SurvivorRatio=8(默认8,即Eden:S0:S1=8:1:1),减少Survivor溢出导致的提前晋升
- 检查是否存在短生命周期大对象直接分配进老年代,可结合-XX:+PrintGCDetails日志中的“direct allocation”线索定位
抑制浮动垃圾,降低预留空间实际占用
并发标记开始后新产生的、未被本轮回收的长期存活对象,就是浮动垃圾。它们悄悄吃掉本该留给新晋升对象的安全空间:
- 避免在CMS周期内密集打DEBUG日志、JSON序列化生成大量临时对象
- 检查缓存组件(如Guava Cache、Caffeine)是否未设size上限或expire策略,造成批量加载时老年代瞬时暴涨
- 启用-XX:+CMSScavengeBeforeRemark,在重新标记前强制一次YGC,清理掉刚晋升但已不可达的对象,间接缓解浮动垃圾压力
监控与兜底:看清问题再动手
不看日志就调参,等于蒙眼开车。关键要看清是否真发生了并发失败:
- GC日志中出现concurrent mode failure或Concurrent Mode Failure字样
- 紧随其后是[Full GC (Allocation Failure)],且real耗时远高于user(典型STW特征)
- 日志里缺少CMS-concurrent-sweep等完整阶段记录,说明CMS流程被中断
- 长期来看,CMS已是JDK14移除的回收器,生产环境建议规划迁移到G1或ZGC
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











