promotion failed 表明年轻代对象因 survivor 区过小、存活率高或老年代碎片化而无法正常晋升,被迫触发 full gc;需通过 jstat 监控 s0u/s1u≈0、ec

频繁出现 Promotion Failed,说明年轻代对象在 Minor GC 后无法顺利晋升到老年代,JVM 被迫触发 Full GC。这不是“GC 没干活”,而是“晋升通道堵死了”——关键在 Survivor 区容量、对象存活率、老年代碎片三者之间的失衡。
盯住 jstat 的三项异常信号
执行 jstat -gc <pid> 1000 5</pid>,重点关注以下组合是否同时出现:
- S0U/S1U 持续接近 0:Survivor 区几乎不存对象,说明存活对象没机会“熬年龄”,直接溢出
- EC(Eden 使用率)长期低于 70% 却高频 YGC:Eden 还没填满就反复回收,典型高存活率特征
- 每次 YGC 后 OU(Old Used)明显跳涨 + S0U ≈ 0:等于实锤“全量晋升”,对象根本没走正常年龄路径
从 GC 日志定位晋升量与空间缺口
开启 -XX:+PrintGCDetails 后,在 Promotion Failed 前的 Minor GC 日志行中提取两个关键数字:
- 计算真实晋升量:日志中
1234567K->1123456K的差值(即老年代增长量) - 估算老年代剩余空间:用总老年代容量减去 GC 前 OU 值
- 若剩余空间数值足够但依然失败,说明是 连续空闲空间不足,即老年代已严重碎片化
检查三项常见配置陷阱
这些参数会人为加剧晋升压力,不是调大堆就能解决:
-
-XX:SurvivorRatio=16:Survivor 区仅占年轻代 1/18,极易装不下转移对象 -
-XX:MaxTenuringThreshold=0 或 1:强制对象首轮 GC 就晋升,绕过 Survivor 缓冲 -
-Xmn 过小 或 -XX:NewRatio 设置不合理:年轻代整体偏小,Eden 空间紧张,Minor GC 频繁且无效
结合堆快照确认强引用滞留
当 O(老年代使用率)超过 80%,立即执行:
-
jmap -dump:live,format=b,file=heap.hprof <pid></pid>(加live参数确保只抓活跃对象) - 用 MAT 打开后,按 Retained Heap 排序,重点看前 10 大对象
- 对可疑对象做 Path to GC Roots 分析,确认是否被静态 Map、ThreadLocal、未注销监听器等强引用长期持有










