老年代空间分配担保机制是在minor gc开始前强制执行的安全预判,判断老年代最大连续空闲空间是否≥新生代所有对象总大小或历史平均晋升大小;仅serial/parallel scavenge收集器启用,不满足则触发full gc。

老年代空间分配担保不是等 Minor GC 完了再“补救”,而是在它开始前就做一次关键预判——判断本次 GC 后可能晋升的对象,老年代到底能不能装得下。核心目标是避免晋升失败导致的突发 Full GC,尤其在 Survivor 区根本塞不下存活对象时。
担保触发的前提条件
这个机制只在使用 Serial、Parallel Scavenge 等基于复制算法的年轻代收集器时生效;G1、ZGC 等不走这套逻辑。它也不是可选项,而是 JVM 强制执行的安全检查,发生在每次 Minor GC 启动前。
- 新生代必须有对象需要晋升(即 Survivor 区容量不足)
- JVM 正在使用支持该机制的垃圾收集器
- 未禁用相关策略(JDK 8u40 起 -XX:+HandlePromotionFailure 已移除,默认始终启用)
担保怎么判断“够不够”
判断依据不是老年代剩余总空间,而是最大一块连续空闲空间,同时结合两个层级的大小比对:
- 第一层:如果老年代最大连续空闲空间 ≥ 当前新生代所有对象总大小(Eden + From + To 中待处理对象),则担保绝对安全,直接允许 Minor GC 执行
- 第二层:如果不满足第一层,但老年代最大连续空闲空间 ≥ 历次 Minor GC 晋升到老年代对象的平均大小,就允许“冒险担保”——这次 Minor GC 可以继续,但存在晋升失败风险
注意:平均晋升大小由 JVM 自动统计并维护(存于 _avg_promoted_size),不是靠人工估算。
担保成功后对象怎么进老年代
所谓“借空间”,不是把对象先挪到 Survivor 再搬去老年代,而是绕过 Survivor 直接落库:
- Eden 区中那些复制到 Survivor 会溢出的对象,直接进入老年代
- From Survivor 区里年龄未达阈值、但总量太大挤不进 To Survivor 的对象,也全部直送老年代
- 哪怕某个对象才 2 岁,只要 Survivor 整体扛不住,它也会被“破格提拔”
担保失败会怎样
“失败”分两种情况:
- 预判阶段就不满足任何条件:JVM 不执行 Minor GC,直接触发 Full GC 来腾空间
- 预判通过但实际晋升时发现老年代真装不下(比如这次存活对象远超历史均值):JVM 捕获异常,立刻补一次 Full GC,再重试 Minor GC
常见诱因包括老年代严重碎片化、-XX:MaxTenuringThreshold 设得过高、或未设置 -XX:PretenureSizeThreshold 导致大对象挤爆 Eden。











