Eden区分配失败后,JVM先判定对象是否为大对象(≥-XX:PretenureSizeThreshold阈值),满足则直接尝试老年代分配;否则触发Minor GC,并在老年代连续空间不足时执行分配担保或Full GC。

Eden 区分配失败后,JVM 并不会立刻抛出 OutOfMemoryError,而是启动一套完整的内存分配兜底流程。其中最关键的一环,就是判断该对象是否属于“大对象”,并决定是否跳过新生代、直接在老年代尝试分配。
大对象判定是触发老年代直配的前提
当 Eden 区空间不足时,JVM 首先检查待分配对象的大小是否 ≥ -XX:PretenureSizeThreshold 设置的阈值(默认为 0,即不启用)。只有显式配置了该参数且对象尺寸达标,才会走“老年代直配”路径。
- 典型大对象包括:长度超限的
String、大容量数组(如new byte[4 * 1024 * 1024])、大型缓存容器等 - 注意:该判定发生在分配请求刚进入堆分配逻辑时,早于 Minor GC 触发判断,不依赖 GC 状态
- 若未配置该参数或对象未达阈值,则进入后续的 GC 尝试流程,而非直配老年代
老年代直配失败会触发分配担保检查
即使对象被识别为大对象,JVM 仍需确认老年代是否有足够连续空间容纳它。此时不查“总可用空间”,而是查“最大连续空闲块”——因为大对象需要连续内存。
- 若老年代最大连续空闲区 ≥ 对象大小,直接在老年代完成分配,流程结束
- 若不满足,JVM 不会立即 Full GC,而是回退到“空间分配担保”机制:检查老年代连续空闲空间是否 ≥ 历次晋升到老年代的平均对象大小
- 若满足,尝试一次带风险的 Minor GC(可能晋升更多对象到老年代,腾出空间);否则直接触发 Full GC
绕过新生代复制能避免性能损耗
大对象直配老年代的核心动因,不是单纯“省空间”,而是规避新生代回收机制的固有开销:
- 新生代使用复制算法(如 Serial、Parallel Scavenge),每次 Minor GC 都需拷贝存活对象;一个 4MB 对象每轮 GC 拷贝一次,耗时显著
- 频繁拷贝大对象会加剧 Eden/Survivor 区内存震荡,导致 Minor GC 更频繁,形成恶性循环
- 老年代使用标记-整理或标记-清除算法,更适合管理长期存活、体积庞大的对象,GC 停顿更可控
实际配置建议
启用大对象直配需结合业务特征谨慎设置:
- 设值不宜过小:例如
-XX:PretenureSizeThreshold=1M可能导致大量中等对象误入老年代,加速老年代碎片化 - 推荐从典型大对象尺寸入手:如业务常用
ByteBuffer.allocate(2M),可设为2097152(2×1024×1024) - 必须配合老年代 GC 策略优化:例如使用 G1 或 ZGC,避免 CMS 因碎片导致 Promotion Failure










