当老年代最大连续空闲空间小于年轻代全部对象总大小时,jvm会进入“冒险担保”判定阶段:若支持该机制(serial/parallel scavenge收集器,java 8+默认启用),则比较老年代最大连续空闲空间与历史晋升平均大小(_avg_promoted_size),前者≥后者即允许带风险执行minor gc,否则触发full gc。

当老年代最大连续空闲空间小于年轻代全部对象总大小时,JVM 并不会立刻放弃 Minor GC,而是进入“冒险担保”判定阶段——前提是收集器支持且未禁用该机制(Java 8+ 默认启用)。
关键判定逻辑:不是看总量,而是比连续块与平均值
JVM 此时会查两个数据:
- 老年代当前最大一块连续空闲内存(不是碎片总和)
- 历史 Minor GC 晋升到老年代对象的平均大小(_avg_promoted_size,JVM 自动维护)
只要前者 ≥ 后者,就允许本次 Minor GC 继续执行,但属于“带风险运行”——因为实际晋升量可能远超均值。
哪些收集器走这套流程
该机制仅对使用复制算法的年轻代收集器生效:
- Serial GC(-XX:+UseSerialGC)
- Parallel Scavenge(-XX:+UseParallelGC,默认年轻代策略)
G1、ZGC、Shenandoah 等不依赖此担保逻辑,它们通过 Region 管理或并发整理规避碎片问题。
冒险失败后的真实处理路径
如果 Minor GC 执行中真发生晋升失败(比如这次存活对象暴增),JVM 会:
- 立即捕获 Promotion Failed 异常
- 触发一次 Full GC(回收整个堆)
- Full GC 完成后,重新检查老年代连续空间
- 若满足条件,则重试 Minor GC;否则抛出 OutOfMemoryError: Java heap space
为什么连续空间比总空闲更重要
对象分配需要连续内存块。即使老年代剩余 1.5G,但被碎片切成几十个 20MB 小块,而本次需晋升 1.2G 对象,仍无法容纳——这正是 CMS 收集器易触发担保失败的根本原因。标记-整理类老年代(如 Serial Old、Parallel Old)能缓解该问题,但整理本身有开销。










