Java垃圾回收的内存分配担保是Minor GC前基于历史晋升量和老年代最大连续空闲空间的事前预判机制,而非事后借用老年代空间;它通过三步判断决定是否允许对象直接晋升老年代或触发Full GC。

Java 垃圾回收不“借用”老年代空间,而是通过内存分配担保机制,在 Minor GC 前主动预判老年代是否能承接晋升对象——它不是事后借,而是事前承诺;不扩容、不划拨,只基于连续空闲空间做安全决策。
担保发生在 Minor GC 开始前,不是 GC 中途或之后
JVM 不会等复制完成才发现 Survivor 装不下,而是在 Minor GC 启动瞬间就执行三步预判:
- 统计历史:读取过去几次 Minor GC 实际晋升到老年代的对象总大小,算出平均值
- 测量现状:获取老年代当前最大一块连续空闲内存(注意是“连续”,不是碎片总和)
- 双重比对:
- 若老年代最大连续空闲 ≥ 新生代所有存活对象总大小 → 安全执行 Minor GC,所有对象按规则晋升
- 若小于总量但 ≥ 历史平均值,且 -XX:+HandlePromotionFailure 已启用 → 允许“冒险担保”,继续 Minor GC
- 否则,跳过 Minor GC,直接触发 Full GC 清理老年代
担保成功 = 绕过 Survivor,直送老年代
所谓“借”,实质是改变对象落点路径,而非挪移已有数据:
- Eden 区中,复制到 Survivor 会溢出的对象,不再尝试放入 Survivor,直接进入老年代
- From Survivor 区里,年龄未达阈值但整体容量超限的对象,也全部跳过 To Survivor,直送老年代
- 哪怕某个对象才经历 1 次 GC,只要 Survivor 区整体扛不住,它就被“破格提拔”
担保失败 ≠ 内存泄漏,而是空间连续性告急
当预判通过但实际晋升仍失败(比如本次存活对象远超历史均值),JVM 会立即补一次 Full GC:
立即学习“Java免费学习笔记(深入)”;
- 若已启用担保但失败,捕获异常后马上执行 Full GC,腾出空间再重试 Minor GC
- 若禁用担保(-XX:-HandlePromotionFailure),则 Minor GC 直接中止,立刻 Full GC
- 频繁失败通常指向:SurvivorRatio 过小、对象存活率偏高、CMS 导致老年代碎片严重、新生代设置过小
不同垃圾收集器对担保的依赖程度差异很大
担保机制的实际效果,高度依赖所选 GC 器的空间管理能力:
- CMS:使用标记-清除,不整理内存,老年代易碎片化 → 连续空间难保障 → 担保失败率高,常退化为 Serial Old
- G1:以 Region 为单位分配,不要求绝对连续 → 更少依赖传统担保;Mixed GC 主动回收高收益老年代 Region,降低晋升压力
- ZGC / Shenandoah:并发整理 + 着色指针 → 几乎消除因碎片导致的分配失败,担保判断退居次要地位



















