对象晋升老年代的真实阈值是动态计算的“new threshold”,而非固定配置的maxtenuringthreshold;它由每次minor gc后survivor区各年龄对象累计占比是否超过targetsurvivorratio(默认50%)决定,该值在gc日志中实时体现,且仅在useadaptivesizepolicy启用时生效。

对象晋升老年代的阈值不是固定数字,而是由 JVM 动态计算并实时调整的结果。它既受 -XX:MaxTenuringThreshold 限制,又依赖 Survivor 区实际对象分布和空间压力,关键看 GC 日志里每轮出现的 “new threshold” 值。
动态年龄判定才是真实晋升依据
每次 Minor GC 后,JVM 会按年龄从小到大累加 Survivor 中各档对象大小,一旦累计占比超过 -XX:TargetSurvivorRatio(默认 50%),就以该年龄为临界点——所有 ≥ 该年龄的对象全部晋升。这个临界点就是 “new threshold”,它可能远低于 MaxTenuringThreshold。
- 例如:年龄 1 对象占 Survivor 42%,年龄 2 占 15% → 累计 57% > 50% → new threshold = 2
- 若所有年龄累计都 ≤ 50%,才退回到按 MaxTenuringThreshold 晋升
- 该机制仅在
-XX:+UseAdaptiveSizePolicy开启时生效(HotSpot 默认开启,但显式关闭后整个动态逻辑失效)
MaxTenuringThreshold 只是上限,不是默认值
它不参与日常晋升计算,只起兜底作用。比如设为 6,不代表对象活过 6 次就一定进老年代;如果动态算出 new threshold = 3,那年龄 ≥ 3 就晋升了。
- CMS + ParNew 组合下默认值为 6,Parallel Scavenge 默认为 15
- 对象头中年龄字段只有 4 位,理论最大值为 15,所以设成大于 15 的值无效
- 单纯调高该值,若 Survivor 区长期宽松(如日志总显示 “new threshold 6 (max 6)”),反而可能延迟晋升、增加年轻代 GC 压力
SurvivorRatio 过小会放大晋升波动
当 -XX:SurvivorRatio=2(即 Eden : Survivor = 2:1,每个 Survivor 仅占年轻代 1/4),Survivor 空间极易饱和,导致:
- Minor GC 后存活对象稍多一点,To 区就装不下 → 直接晋升(非年龄原因)
- 年龄 1 对象总量略超 Survivor 一半 → new threshold 骤降至 1 → 大量新生对象提前进老年代
- 老年代快速堆积,尤其在秒杀类流量脉冲场景,易触发频繁 Full GC
更稳妥的做法是设为 -XX:SurvivorRatio=6(Eden : Survivor = 6:2:2),留出足够缓冲空间。
怎么看实际生效的晋升年龄?
启用 -XX:+PrintGCDetails 后,Minor GC 日志中会出现类似:
其中 “new threshold 2” 就是本轮 GC 后真正执行的晋升年龄,它每轮都可能变化。持续观察该值:
- 长期稳定在 max 值 → Survivor 宽松或对象死亡率高
- 频繁出现 1 或 2 → Survivor 过小,或业务存在批量同龄对象(如统一构造的 DTO 列表)
- 突降再回升 → 可能对应某次请求洪峰或缓存预热行为











