cms并发失败根本原因是老年代内存压力与cms并发清理节奏脱节,表现为晋升速率突增、浮动垃圾堆积及触发阈值过高,导致预留空间被击穿,jvm硬编码退化为serial old执行full gc。

CMS并发失败的根本原因不是参数配错了,而是老年代的内存压力节奏和CMS的并发清理节奏脱节了。它本质是一个动态失衡信号:用户线程还在持续分配对象,而CMS还没来得及清理出足够空间,导致预留“安全垫”被击穿。
老年代空间在并发中被快速填满
CMS运行时,年轻代GC仍频繁发生,大量对象晋升到老年代;同时大对象可能直接分配进老年代。如果晋升速率突增(比如流量高峰、缓存批量加载),老年代占用率会在CMS标记或清除阶段迅速攀升,超出预留余量。
- 典型表现是GC日志中出现concurrent mode failure后紧接Full GC,且Cause为Allocation Failure
- 这不是空间总量不够,而是“连续可用空间”不足——CMS用的是标记-清除算法,不压缩,碎片多了,大对象就分不进去
浮动垃圾持续占用预留空间
并发标记开始后,用户线程新产生的对象(尤其是生命周期较长的临时对象)不会被本轮CMS标记回收,形成浮动垃圾。这部分空间必须被提前预留,但实际增长往往超出JVM估算。
- 常见诱因包括:CMS周期内密集打DEBUG日志、无界缓存put、JSON序列化生成大量中间对象
- 浮动垃圾越多,留给新晋升对象的安全空间就越少,触发失败的概率越高
CMS启动太晚,没留够反应时间
-XX:CMSInitiatingOccupancyFraction默认值在JDK7+后会动态调整,有时高达75%甚至80%。等老年代用了这么多才启动CMS,相当于让清理工作在“红灯区”起步,稍有波动就失控。
- 建议显式设为50~60,并搭配-XX:+UseCMSInitiatingOccupancyOnly禁用动态调整
- 配合jstat -gc
观察OU(老年代使用量)和YGC频率,确认晋升是否稳定
退化行为固定走Serial Old,停顿明显
一旦并发失败,JVM硬编码回退到Serial Old执行Full GC——单线程、STW、标记-整理。这不是可选方案,而是HotSpot在JDK8及之前版本中的兜底设计。
- 日志里看不到CMS-concurrent-sweep这类并发阶段记录,只有[Full GC (Allocation Failure)]
- real耗时远大于user,常达数百毫秒以上,业务接口超时、线程堆积都可能由此引发











