单次晋升量=年轻代减少量−整个堆减少量,晋升速率=晋升量÷gc时间间隔;持续高于5mb/s且老年代阶梯上升即为过快晋升,配合new threshold=1/2、s0u/s1u≥95%等特征可快速识别。

直接看每次 Minor GC 日志里的堆内存变化差值,就能算出单次晋升量;再结合时间间隔,就能得出晋升速率(MB/s)。这不是配置出来的数字,而是真实发生的“对象洪流”。
从 GC 日志里提取单次晋升量
打开带 -XX:+PrintGCDetails 的日志,找这类行:
1.514: [GC (Allocation Failure) [PSYoungGen: 38395K->5120K(71680K)] 45665K->35687K(159232K), 0.0570688 secs]关键三组数字:
- 年轻代回收前:38395K(Eden + From 中原有使用量)
- 年轻代回收后:5120K(Survivor 中存活对象)
- 整个堆回收前/后:45665K → 35687K
晋升量 = (年轻代减少量)−(整个堆减少量)
= (38395 − 5120) − (45665 − 35687) = 33275 − 9978 = 23297 KB ≈ 22.7 MB
计算晋升速率(Promotion Rate)
用连续两次 GC 的时间戳相减,得到间隔秒数,再除晋升量:
- 上一轮 GC 时间戳:1.514s
- 下一轮 GC 时间戳:3.018s
- 间隔 = 3.018 − 1.514 = 1.504s
- 晋升速率 = 22.7 MB ÷ 1.504 s ≈ 15.1 MB/s
持续高于 5 MB/s,且老年代使用曲线同步阶梯上升,就是过快晋升的明确信号。
快速识别异常模式的技巧
不用逐行手算,重点关注日志中反复出现的组合特征:
- new threshold 长期为 1 或 2:说明 Survivor 区严重不足,大量 age-1 对象直接晋升
- 每次 GC 后老年代 Used 增加量稳定在 15–30 MB:不是随机波动,而是规律性“灌入”
- GC Cause 是 Allocation Failure,但晋升量却远超 Eden 容量:说明不是 Eden 满了才 GC,而是 Survivor 溢出倒逼提前晋升
配合 jstat 验证更高效
运行:jstat -gc -h10
- S0U / S1U 持续 ≥ 95%:Survivor 区长期打满,是晋升过快的前置表现
- EC(Eden 容量)高但 EU(Eden 已用)增长慢:可能大对象直入老年代,干扰判断,需单独排查
- OGC(老年代总容量)不变,OU(老年代已用)线性上涨:确认晋升是主因,而非元空间或直接内存泄漏










