promotion failed 表明年轻代对象晋升时老年代无足够连续空间,主因大变量引发碎片;需结合日志中 promotion failed 标记、晋升量估算、老年代剩余空间与碎片矛盾定位;通过 -xx:+printtenuringdistribution 等参数确认大变量主导,并采用预分配、生命周期管控、碎片整理等策略优化。

看到 GC 日志里出现 Promotion Failed,基本可以确定:年轻代 GC 后,本该晋升到老年代的对象“卡住”了——不是因为老年代完全满了,而是它拿不出一块足够大的连续空闲空间来容纳这批对象。大变量(比如大数组、大缓存、长字符串等)往往是导火索,它们体积大、生命周期长,一晋升就占一大块地,特别容易撞上碎片。
从日志定位大变量晋升的关键线索
重点看三类信息:
-
ParNew 行带 (promotion failed) 标记:例如
[ParNew (promotion failed): 146112K->146112K(153600K), ...]—— 年轻代几乎没回收(前后大小几乎不变),说明对象全堆在 Eden/Survivor 里,等着“一步到位”进老年代,但失败了; -
晋升量估算值:日志中通常隐含或可通过前后对比算出本次需晋升的大小,比如
1234567K - 892345K = 342222K,这个量级已接近或超过常见大对象阈值(如 > 512KB); - 老年代剩余空间与碎片矛盾:日志显示老年代总剩余空间充足(如 973696K),但仍报失败——这是典型碎片信号,而大变量正是碎片的“放大器”,它们无法被拆分,必须整块分配。
确认是否是大变量主导的晋升压力
启用关键 JVM 参数获取更细粒度证据:
AI 舌诊面诊问诊医疗系统 + 诊疗商城系统源码是一套结合 AI 辅助诊断与商品转化的健康类程序,基于浩创云、ThinkPHP(PHP)开发,支持微信服务号与 H5 端,适合做中医舌诊面诊问答、健康建议推送以及配套诊疗商品的运营。
-
-XX:+PrintTenuringDistribution:观察每次 YGC 后各年龄对象大小,若 age=1 或 age=2 就出现数百 KB 甚至 MB 级别对象,大概率是大变量提前晋升; -
-XX:+PrintGCDetails -XX:+PrintHeapAtGC:比对 GC 前后老年代使用量突增幅度,若单次增长超 200MB 且伴随 promotion failed,高度提示大对象集中晋升; -
-XX:+UseCompressedOops -XX:ObjectAlignmentInBytes=8(必要时):辅助估算大数组实际内存占用,避免因指针压缩或对齐导致的误判。
针对性解决大变量引发的晋升失败
不靠盲目调大堆,而是控源头、疏路径、防碎片:
-
拦截大对象直接分配到老年代:用
-XX:PretenureSizeThreshold(仅限 Serial/Parallel GC)或-XX:+UseG1GC -XX:G1HeapRegionSize=1M(G1 下控制 Humongous 区触发)让大对象绕过年轻代,减少晋升冲击; -
限制大变量生命周期:检查代码中类似
new byte[1024 * 1024 * 10]、大 Map 缓存、未关闭的流式数据结构,改用池化、分片、及时置 null 或软引用; -
降低老年代碎片敏感度:CMS 场景下启用
-XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=1,强制每次 Full GC 后整理;G1 场景则合理设置-XX:G1ReservePercent=15(大促类业务),预留缓冲防突发晋升压垮并发标记。
验证优化是否生效
不要只看 Full GC 次数降没降,盯住三个硬指标:
- 日志中
(promotion failed)出现频率归零或显著下降; - 老年代每次 CMS GC 后的
used值波动变平缓,不再出现单次猛增 200MB+; - 用 jstat -gc 实时观察
OGC(老年代已用容量)曲线,锯齿状尖峰减少,斜率趋稳。










