分配担保机制的核心作用是minor gc前预判老年代连续空间是否足够,避免晋升失败:先比新生代对象总量,再比历史晋升平均值,不足则触发full gc。

新生代对象分配失败时,分配担保机制的核心作用是让老年代临时“接住”那些无法留在新生代的存活对象,避免直接触发 OOM 或卡死在 GC 流程中。它不是事后补救,而是在 Minor GC 前就做预判、提前兜底,本质是用老年代的空间确定性,换新生代 GC 的执行确定性。
为什么新生代需要老年代来兜底
新生代使用复制算法(Eden + Survivor),GC 时必须把存活对象从 From 区复制到 To 区。但 To 区空间有限,当一次 Minor GC 后存活对象太多,Survivor 容不下时,这些对象就必须晋升。可晋升的前提是老年代有地方放——不是看总空闲大小,而是看最大连续空闲块是否够用。一旦不满足,整个 Minor GC 就可能失败。担保机制正是为解决这个断点而设。
担保怎么判断要不要走 Full GC
判断分两步,且发生在 Minor GC 开始前:
- 先查老年代最大连续空闲空间是否 ≥ 新生代所有对象总大小:满足则安全执行 Minor GC;
- 不满足时,再比对老年代最大连续空闲空间是否 ≥ 历次晋升对象的平均大小(基于历史 GC 日志统计);
- 若仍不满足,或 JVM 配置不允许冒险(如旧版本 HandlePromotionFailure=false),就会先触发 Full GC,清理老年代后再重试 Minor GC。
担保失败不等于内存泄漏
常见诱因其实是配置或负载失衡:
- SurvivorRatio 设置过小(比如设成 2),导致 Survivor 区太窄,对象频繁溢出;
- 对象平均寿命偏长,大量对象熬过多次 GC 还活着,持续挤压 Survivor;
- 新生代太小或瞬时创建大量对象,Eden 频繁打满,Minor GC 太密,Survivor 来不及清理;
- 老年代碎片多(尤其 CMS 收集器),总空闲足够但连续空间不足,担保自然失败。
调优关键看什么日志
开启 -XX:+PrintGCDetails 后,关注 GC 日志中的关键词:
- 出现 Allocation Failure 表示 Eden 满触发 Minor GC;
- 看到 PSYoungGen: xxxK->0K 但 ParOldGen 数值明显上升,说明有对象成功晋升;
- 若紧接着出现 Full GC (Ergonomics) 或 Full GC (Allocation Failure),大概率是担保失败后触发的兜底动作。











