Java堆分为年轻代和老年代,年轻代专为短命对象设计,采用复制算法,含Eden区和两个Survivor区;老年代存放长寿对象,GC频率低但耗时长,使用标记-整理或标记-清除算法;二者通过年龄晋升、动态年龄判定、担保机制和大对象直入等规则协同工作。

Java 堆是 JVM 中所有线程共享的、最大的内存区域,用于存放对象实例(包括数组)、字符串常量池和静态变量。JDK 8 之后,堆的逻辑结构明确分为年轻代和老年代,而方法区由永久代(PermGen)彻底替换为元空间(Metaspace),且元空间不再属于堆内存——它位于本地内存(直接内存)中。
年轻代:专为“短命对象”设计的高效回收区
年轻代占堆内存约 1/3~1/4(可通过 -XX:NewRatio 或 -Xmn 显式设置),默认采用复制算法(Copying),目标是快速清理大量朝生夕死的对象。它由三部分组成:
-
Eden 区:新对象绝大多数在此分配(如
new Object()); -
两个 Survivor 区(S0 和 S1):大小相等、角色动态互换;默认 Eden : S0 : S1 = 8 : 1 : 1(可通过
-XX:SurvivorRatio=8调整); - 每次 Minor GC 时,Eden 和当前 From Survivor 中存活的对象被复制到 To Survivor;年龄+1;To Survivor 满或对象年龄 ≥
-XX:MaxTenuringThreshold(默认 15)时,晋升至老年代。
老年代:承载“长寿对象”的稳定存储区
老年代存放从年轻代晋升过来的长期存活对象(如缓存、单例、大集合)、以及直接分配的大对象(超过 -XX:PretenureSizeThreshold 阈值)。它的特点是:
- 空间更大,GC 频率低,但每次 Major GC(或 Full GC)耗时长、停顿(STW)显著;
- 使用标记-整理(Mark-Compact)或标记-清除(Mark-Sweep)算法(取决于所选垃圾收集器,如 Serial Old、CMS、G1);
- 触发条件包括:老年代空间不足、Minor GC 后无法容纳晋升对象、显式调用
System.gc()(不保证执行)等。
年轻代与老年代协同工作的关键机制
二者不是孤立存在,而是通过一套晋升规则紧密联动:
立即学习“Java免费学习笔记(深入)”;
- 年龄晋升:对象每经历一次 Minor GC 且存活,年龄加 1;达到阈值即进入老年代;
-
动态年龄判定:HotSpot 会统计各年龄段对象总大小,若某年龄及更小年龄的对象累计占用 Survivor 空间 > 50%,则将该年龄设为新晋升阈值(取其与
MaxTenuringThreshold的较小值); - 担保机制:Minor GC 前,JVM 检查老年代剩余空间是否 ≥ 年轻代全部对象总和(或历史晋升平均值),否则提前触发 Full GC;
- 大对象直入老年代:避免在年轻代反复复制,提升分配效率(如超大数组)。
实际配置与观察建议
理解划分后,调试和调优更有的放矢:
- 用
-XX:+PrintGCDetails -XX:+PrintGCTimeStamps查看 GC 日志,确认年轻代/老年代使用率与 GC 类型(Young GC / Full GC); - 配合
-XX:+PrintTenuringDistribution观察对象年龄分布,判断MaxTenuringThreshold是否合理; - 避免频繁 Full GC:关注老年代增长趋势,检查是否存在内存泄漏或缓存未清理;
- JDK 8+ 不再有永久代,类元数据(Class、Method、Constant Pool)全归元空间管理,需用
-XX:MetaspaceSize和-XX:MaxMetaspaceSize控制其大小。


















