java堆按对象生命周期划分为新生代(占1/3)和老年代(占2/3),新生代含eden(80%)与两个survivor区(各10%),对象经minor gc存活达阈值或大对象直接晋升老年代。

Java 堆内存按对象生命周期特征,逻辑上划分为新生代(Young Generation)和老年代(Old Generation),两者共同构成堆的全部可用空间(即 -Xms 到 -Xmx 所设范围),不包含元空间(Metaspace)。
新生代用于存放新创建的对象,多数对象在此“朝生夕灭”;
老年代则容纳长期存活、经多次 GC 仍健在的对象,以及直接分配的大对象。
新生代与老年代的比例由参数控制
默认情况下,HotSpot JVM 设置 -XX:NewRatio=2,表示:
老年代大小 ÷ 新生代大小 = 2
→ 新生代占堆总大小的 1/3,老年代占 2/3。
计算公式为(设堆最大大小为 -Xmx,-XX:NewRatio=n):
- 新生代大小 = 堆总大小 ÷ (n + 1)
- 老年代大小 = 堆总大小 × n ÷ (n + 1)
例如:-Xmx4g -XX:NewRatio=3 → 新生代 = 4GB ÷ 4 = 1GB,老年代 = 3GB。
⚠️ 注意:
-
-Xmn参数会完全覆盖NewRatio的计算结果。写了-Xmn1g,JVM 就直接固定新生代为 1GB,不再理会NewRatio。 - G1、ZGC 等现代收集器忽略
NewRatio,它们自主管理分区,该参数无效。 - CMS 收集器下
NewRatio有时失效,建议用-Xmn显式指定新生代大小。
新生代内部结构:Eden + 两个 Survivor 区
新生代进一步划分为:
-
Eden 区:新对象默认分配地,占新生代默认 80%(由
-XX:SurvivorRatio=8决定,即 Eden : S0 : S1 = 8 : 1 : 1)。 - Survivor 区(S0 和 S1):大小相等、角色互换,每次 Minor GC 后,存活对象从 Eden + 当前 From 区复制到空闲 To 区,并增加 GC 年龄。
实际可用新生代空间 ≈ 90%,因为任意时刻总有一个 Survivor 是空闲的。
对象如何在两代之间流动?
- 对象首先在 Eden 分配;
- Eden 满触发 Minor GC,存活对象移入 Survivor 区,年龄置为 1;
- 后续 Minor GC 中,Survivor 区存活对象在 S0/S1 间复制,年龄递增;
- 达到
MaxTenuringThreshold(默认 15)或 Survivor 空间不足时,对象晋升至老年代; - 大对象(超过
-XX:PretenureSizeThreshold)直接进入老年代; - Survivor 区中,若某年龄批次对象总和 ≥ Survivor 空间一半,该年龄及以上对象立即晋升(动态年龄判定)。
调优关键点不是只看比例
- Survivor 过小会导致对象“早晋升”:Minor GC 后
Promoted量异常高、老年代增长快、S0/S1 使用率长期 >90%,说明 Survivor 不够用。 - 解决方法:调大
-Xmn,或减小-XX:SurvivorRatio(如设为 6,使每个 Survivor 占新生代约 14%)。 - 验证手段:加
-XX:+PrintGCDetails,观察 GC 日志中的Promoted、S0U/S1U、EU等字段变化趋势。
不复杂但容易忽略。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











