在 JVM 的分代垃圾回收机制中,对象会在 Survivor 区(S0/S1)之间反复复制。每经过一次 Minor GC 且存活,对象的年龄(Age)就会加 1。当年龄达到阈值(默认 15)时,对象会被晋升到老年代。

面试官经常问:“为什么默认是 15?我能把它改成 25 吗?” 这道题考察的是对 Java 对象内存布局 的底层理解。

1. 核心概念

JVM 中对象的内存布局主要包含三部分:

  1. 对象头(Object Header)
  2. 实例数据(Instance Data)
  3. 对齐填充(Padding)

其中,对象头包含两部分信息:

  • Mark Word:存储哈希码、GC 分代年龄、锁状态标志等。
  • Klass Pointer:指向类元数据的指针。

问题的答案就隐藏在 Mark Word 的结构中。


2. 原理与源码分析

2.1 Mark Word 的位定义

在 64 位虚拟机(开启压缩指针)中,Mark Word 占用 8 字节(64 bit)。虽然不同锁状态下 Mark Word 的布局不同,但在无锁状态下,其结构大致如下:

Bit Range Meaning
25 bit unused
31 bit hashCode
1 bit unused
4 bit GC Age
1 bit biased_lock
2 bit lock

关键点来了: JVM 在 Mark Word 中仅分配了 4 个 bit 来存储对象的 GC 年龄。

2.2 二进制计算

根据二进制计算:

  • 4 bit 能表示的最大无符号整数是 1111(二进制)。
  • 转换成十进制就是 8 + 4 + 2 + 1 = 15

因此,对象的分代年龄最大只能存储到 15。这是由底层存储结构决定的硬性限制。


3. 尝试设置为 25 会发生什么?

我们可以通过 JVM 参数 -XX:MaxTenuringThreshold 来调整这个阈值。

如果你在启动 JVM 时尝试设置:

java -XX:MaxTenuringThreshold=25 -jar app.jar

结果是:JVM 启动失败,并报错。

Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.
MaxTenuringThreshold of 25 is invalid; must be between 0 and 15

JVM 在启动参数校验时,会明确检查该值是否在 0 到 15 之间。


4. 动态年龄判定(补充考点)

虽然最大年龄是 15,但对象不一定非要达到 15 岁才会晋升老年代。JVM 还有动态对象年龄判定机制:

如果在 Survivor 空间中,相同年龄所有对象的大小总和大于 Survivor 空间的一半(TargetSurvivorRatio,默认 50%),那么年龄大于或等于该年龄的对象就可以直接进入老年代,无须等到 MaxTenuringThreshold。


5. 总结

面试回答总结:

JVM 分代年龄限制为 15,根本原因是对象头(Object Header)的 Mark Word 结构设计。 在 Mark Word 中,只预留了 4 个 bit 的空间来存储 GC 年龄。因为 4 个 bit 能表示的最大数值是 1111(即 15),所以最大年龄无法超过 15。 如果尝试通过 -XX:MaxTenuringThreshold 设置为大于 15 的值(如 25),JVM 会在启动时校验失败并报错。 此外,实际晋升还受动态年龄判定规则影响,不一定非要达到最大阈值。