JVM年轻代分为Eden与两个Survivor区(默认8:1:1),对象经多次Minor GC存活(默认15次)或Survivor空间不足时晋升老年代;大对象可直入老年代;老年代空间不足将触发Full GC,失败则抛OutOfMemoryError。

年轻代和老年代的空间划分与晋升机制,是理解JVM内存行为的关键。它不是凭空设定的规则,而是围绕“大多数对象朝生夕死”这一真实现象设计的优化策略。
年轻代内部怎么分:Eden + 两个 Survivor
年轻代默认占整个堆的 1/3(可通过 -XX:NewRatio 调整),内部再细分为三块:
- Eden 区:占年轻代约 80%,新对象几乎都分配在这里;
- Survivor 0(From)和 Survivor 1(To):各占约 10%,大小相等,默认比例 Eden:S0:S1 = 8:1:1(由 -XX:SurvivorRatio=8 控制);
- 每次 Minor GC 后,“空”的 Survivor 区会切换角色,始终保证 To 区为空,用于接收存活对象。
对象什么时候从年轻代升到老年代
晋升不是按时间,而是按“经历多少次 Minor GC 仍存活”,即对象年龄。满足任一条件就会晋升:
- 年龄达标:默认经历 15 次 Minor GC(年龄达 15),可通过 -XX:MaxTenuringThreshold=N 修改,例如设为 6,表示活过 6 轮 GC 就进老年代;
- Survivor 放不下:某次 GC 后,相同年龄的所有对象总大小超过 Survivor 空间一半,那么所有 ≥ 该年龄的对象直接晋升;
- 大对象直入老年代:对象大小超过 -XX:PretenureSizeThreshold(如设为 1048576 即 1MB),跳过年轻代,直接在老年代分配;
- 分配担保失败:Minor GC 时 Eden + From 中存活对象太多,To 区装不下,多余部分直接晋升。
老年代空间不够会发生什么
晋升前 JVM 会检查老年代是否有足够连续空间。若不够:
- 先尝试触发一次 Major GC(即只清理老年代),但 HotSpot 中多数收集器(如 Parallel、G1)不单独执行 Major GC;
- 实际更常见的是直接触发 Full GC,对整个堆(年轻代 + 老年代 + 元空间)做回收;
- 如果 Full GC 后仍无法腾出足够空间容纳待晋升对象,就会抛出 java.lang.OutOfMemoryError: Java heap space。
几个实用的观察和调优建议
想验证晋升行为,可以加这些 JVM 参数启动应用:
- -XX:+PrintTenuringDistribution:每次 GC 后打印各年龄对象大小分布;
- -XX:+PrintGCDetails -XX:+PrintGCDateStamps:查看 GC 类型、耗时、各代使用量变化;
- 配合 -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8 这类固定参数做小实验,能清晰看到 Eden 满→Minor GC→对象移入 Survivor 或晋升的过程。

















