promotion failed 和 concurrent mode failure 表明 cms/g1 老年代管理失衡:前者因碎片化导致晋升失败,后者因并发回收跟不上内存增长;需通过 gc 日志分析晋升量、碎片程度与并发周期耗时,优先考虑升级至 g1 或 zgc。

遇到 Promotion Failed 或 Concurrent Mode Failure,说明 CMS 或 G1 垃圾收集器在老年代空间管理上已出现明显失衡——不是老年代碎片化严重,就是晋升压力远超预期容量。这类错误不是偶然触发的告警,而是系统长期积累的调优盲区暴露出来的结果。
看清 Promotion Failed 的真实诱因
Promotion Failed 发生在 Minor GC 时,年轻代对象要晋升到老年代,但老年代没有足够连续空间容纳这批对象(哪怕总剩余空间充足)。常见于 CMS 收集器,本质是老年代碎片化 + 晋升担保失败。
- 检查 GC 日志中是否伴随大量
ParNew (promotion failed),同时老年代使用率并不高(比如才 60%),这就是典型碎片问题 - CMS 默认不会压缩老年代,连续空闲块不足时,即使总空闲空间够,也无法完成晋升
- 注意 Survivor 区设置:如果
-XX:SurvivorRatio过大或-XX:MaxTenuringThreshold设置不合理,会导致过早或过晚晋升,加剧老年代压力 - 临时缓解可用
-XX:+UseCMSCompactAtFullCollection+-XX:CMSFullGCsBeforeCompaction,但这是治标;根本解法是控制晋升节奏或换用 G1/ZGC
识别 Concurrent Mode Failure 的关键信号
Concurrent Mode Failure 出现在 CMS 并发标记阶段未完成,老年代就已耗尽,被迫退化为 Serial Old 全停顿收集。它反映的是并发回收跟不上内存增长速度。
- 日志中出现
concurrent mode failure同时伴随老年代使用率快速逼近 92%(CMS 默认触发阈值),说明初始触发时机太晚 -
-XX:CMSInitiatingOccupancyFraction设置过高(如设为 90)是主因之一;建议从 70–75 开始压测,结合应用对象生命周期动态调整 - 注意 CMS 的 concurrent cycle 耗时:若单次并发周期 > 应用分配速率 × 老年代剩余空间,就会大概率失败
- 频繁发生时,可开启
-XX:+PrintGCDetails -XX:+PrintGCTimeStamps对比两次 Full GC 间隔与并发周期时间差,判断是否持续“追赶不上”
用 GC 日志定位调优盲区的实操要点
光看报错不够,需从日志中提取三类关键数据交叉验证:
-
晋升量统计:每轮 ParNew 日志中的
object size和promoted字段,计算平均晋升大小和频率,反推老年代需预留的“安全缓冲区” -
碎片量化:CMS 下启用
-XX:+PrintGCDetails后,观察 Full GC 前后老年代的used和capacity变化;若 Full GC 后 used 显著下降(如 800MB→200MB),说明碎片严重 -
并发周期节奏:记录
concurrent-mark、concurrent-preclean、concurrent-sweep各阶段耗时,若 preclean 阶段异常长,往往意味着年轻代 GC 太频繁,导致老年代引用更新风暴
绕过盲区的调优路径选择
当发现 CMS 难以稳定规避这两类失败,不要硬调参数,应评估升级回收器的可行性:
- G1 在 JDK 8u20+ 后已成熟,通过
-XX:MaxGCPauseMillis控制停顿,自动管理分区,天然规避 Promotion Failed(晋升走 Region 分配,不依赖连续空间) - ZGC/Shenandoah(JDK 11+)更进一步,几乎消除并发失败风险,适合堆大于 4GB、延迟敏感场景
- 若必须沿用 CMS,可配合
-XX:+UseCMSInitiatingOccupancyOnly锁定触发逻辑,并用-XX:CMSTriggerRatio动态微调,但需监控 long-term allocation rate 稳定性
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











