“promotion failed”表明年轻代对象因老年代内存碎片无法晋升,非总空间不足;需结合parnew失败日志、老年代碎片化使用量及后续full gc或cms失败等连锁反应综合判断。

Java GC 日志里出现“Promotion Failed”,说明年轻代对象在 Minor GC 后无法晋升到老年代——不是老年代总空间不够,而是**拿不出一块连续空闲空间**来放下这批对象。这是内存碎片严重的直接信号,必须结合多处日志细节交叉判断,不能只看字面。
盯住 ParNew 行里的关键矛盾
重点找带 (promotion failed) 标记的 ParNew 日志行,例如:
[ParNew (promotion failed): 146112K->146112K(153600K), 0.1234567 secs]
这行藏着三个关键线索:
- 前后大小几乎不变(146112K→146112K),说明 Eden 和 Survivor 几乎没回收任何对象,全等着“一步到位”进老年代,但被拦下了;
- 括号里明确标出失败类型,不是 Allocation Failure 或 Concurrent Mode Failure,定位更精准;
- 耗时通常偏长(>0.1s),反映 JVM 在反复尝试寻找连续空间未果。
比对老年代使用量与剩余空间
紧接 ParNew 行之后,看老年代(PSOldGen、ParOldGen 或 CMS Old Gen)的日志段,例如:
[PSOldGen: 8200M->8200M(12288M)]
这里能看出:
- 已用 8200M,总容量 12288M,剩余约 4088M,理论上够放几百 MB 对象;
- 但晋升仍失败,证明这 4GB 是碎的,拼不出一块 ≥ 晋升对象大小的连续块;
- 若单次晋升量估算值(如 GC 前后老年代差值)达 200MB 以上,基本可锁定是大对象引发的碎片放大效应。
关注紧随其后的连锁反应
Promotion Failed 很少单独发生,常触发更严重的后续行为:
- 立刻跟一次 Full GC(尤其是 Serial Old 回收),日志中出现 [Full GC (System.gc())] 或 [Full GC (Allocation Failure)];
- 出现 Concurrent Mode Failure,说明 CMS 还没完成并发清理,老年代就因碎片+新对象涌入彻底失守;
- GC 日志中 Survivor 使用率长期接近 0(S0U/S1U ≈ 0),说明对象根本没在 Survivor 停留,直奔老年代,加剧碎片压力。
启用辅助参数让日志“说人话”
默认日志信息有限,加这几个参数才能看清本质:
- -XX:+PrintTenuringDistribution:看每次 YGC 后 age=1 或 age=2 就出现 MB 级对象,就是大变量提前晋升的实锤;
- -XX:+PrintGCDetails -XX:+PrintHeapAtGC:对比 GC 前后老年代使用量突增幅度,确认是否集中晋升;
-
jstat -gc
1000 5 :观察 OU 阶梯式跳涨 + S0U/S1U 持续为 0,是绕过 Survivor 的强证据。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











