Minor GC时若Survivor装不下存活对象,JVM启动内存分配担保机制:预估老年代连续空闲空间是否足够容纳新生代全部存活对象,足够则直接晋升,不足但满足冒险条件则尝试担保,否则触发Full GC。

当新生代 Minor GC 后,大量对象存活,Survivor 区装不下,JVM 就会启动内存分配担保机制——本质是“向老年代临时借空间”,让本该晋升的对象顺利落脚,避免直接崩溃或强制 Full GC。
担保触发的两个硬性前提
不是每次 Survivor 满了都能借,必须满足以下任一条件:
- 老年代最大可用连续空间 ≥ 当前新生代所有对象总大小(含 Eden + 两个 Survivor 中待处理对象)——这时担保绝对安全,直接走晋升流程
- 老年代最大可用连续空间 < 新生代对象总大小,但 ≥ 历次 Minor GC 晋升到老年代对象的平均大小,且 -XX:+HandlePromotionFailure 已启用(Java 8+ 默认开启)——这时“冒险担保”,允许尝试 Minor GC
借的过程:不是等 GC 完再判断,而是前置预估
JVM 不会在 GC 执行一半才发现没地方放。它在 Minor GC 开始前就做三件事:
- 查历史数据:统计过去几次 Minor GC 实际晋升到老年代的对象大小,算出平均值
- 量老年代:获取当前老年代最大一块连续空闲内存(注意是“连续”,不是碎片总和)
- 比大小:用平均值去比对老年代连续空闲空间;够,就放行 GC;不够,又不允许冒险,就跳过 Minor GC,直接 Full GC
借成功后,对象怎么进老年代?
借不是靠“挪”,而是绕过 Survivor 直接落库:
- Eden 区存活对象中,凡是复制到 Survivor 会溢出的,直接进入老年代
- 原 Survivor 区(比如 S1)中年龄未达阈值但总量太大、挤不进目标 Survivor(S0)的,也全部直送老年代
- 哪怕某个对象才 2 岁,只要 Survivor 区整体扛不住,它也会被“破格提拔”
借失败了怎么办?
所谓“失败”,是指担保判断后仍发生晋升异常——比如这次存活对象暴增,远超历史平均值,老年代真装不下:
- 如果 -XX:-HandlePromotionFailure(禁用担保),则 Minor GC 立即中止,直接触发 Full GC
- 如果已启用担保但实际失败,JVM 会捕获异常,马上补一次 Full GC 来腾空间,再重试 Minor GC
- 频繁失败往往意味着老年代碎片化严重,或 Survivor 设置过小、对象存活率偏高,需调参或分析对象生命周期


















