Survivor空间中,何时自动晋升基于相同年龄变量总和过半的判定逻辑?
- 内容介绍
- 相关推荐
本文共计942个文字,预计阅读时间需要4分钟。
动态对象年龄决定不单纯是存活几轮就走,而是看+Survivor+区域内某一年龄段的对象是否已将近填满。只要这一年龄段的对象占总大小+Survivor+总容量的一半(默认50%),则该年龄段的对象在下一次Minor GC后直接进入老年代。
晋升触发的关键条件
这个机制不跨年龄累加,也不看平均或预测,只做一次硬性扫描和判断:
- Minor GC 结束后,JVM 扫描当前 Survivor 区中所有存活对象,按年龄分组统计大小
- 从 age=1 开始,逐档累加:age1 + age2 + … 直到累计值首次 ≥ Survivor 容量 × TargetSurvivorRatio(默认 0.5)
- 此时的年龄就是新阈值,比如累加到 age3 达标,则 age3 及以上全部晋升
- 注意:必须是“某一个具体年龄档”的累计值达标,不是 age1 占 40%、age2 占 20% 合起来算——那是错误理解
GC 日志怎么看实际晋升年龄
打开 -XX:+PrintGCDetails 后,Minor GC 日志中会出现类似这行:
Desired survivor size 524288 bytes, new threshold 3 (max 6)
其中 new threshold 3 就是本次动态计算出的晋升起点年龄。它每轮 GC 都可能变:
- 如果长期显示 new threshold 6 (max 6),说明 Survivor 空间宽松,没触发动态逻辑
- 如果频繁出现 new threshold 1 或 2,大概率是 Survivor 过小,或某批对象生命周期高度一致(如批量请求生成的 VO)
- 日志中还会打印各年龄对象明细,例如:age 1: 262144 bytes, 262144 total —— 这表示 age1 对象共 256KB,若 Survivor 是 512KB,就已超半,直接触发 age1 晋升
为什么不是“所有 age≥X 的对象都该走”?要结合空间压力
这个机制本质是空间保底策略:防止 Survivor 区被大龄对象“卡死”,导致后续小对象无处可放。它的出发点不是对象该不该老,而是“Survivor 还能不能用”。所以:
- 即使你设了 -XX:MaxTenuringThreshold=15,只要 age2 对象占满 Survivor 一半,JVM 就会无视 15,强制 age2+ 晋升
- 如果 Survivor 区本身太小(比如 -XX:SurvivorRatio=2),age1 对象稍多一点就容易超半,导致大量新生对象“一岁就进老年代”
- 这种晋升和担保失败不同:它不依赖空间是否溢出,而是在复制前就根据分布预判;担保失败则是 To 区真装不下,属于兜底行为
调优时要注意的配合点
单改 MaxTenuringThreshold 基本无效,必须和 Survivor 空间配置联动:
- -XX:TargetSurvivorRatio 控制判定比例,默认 50%,可微调(如设为 60% 可减少误触,但需确保 Survivor 有余量)
- -XX:SurvivorRatio 决定 Survivor 大小,建议从默认 8 放宽到 6 或 4(即 Eden:Survivor = 6:2),让每个 Survivor 占年轻代 1/4,留出缓冲
- 必须启用 -XX:+UseAdaptiveSizePolicy(默认开启),否则整个动态机制不生效,退化为固定阈值
- 监控关键指标:用 jstat -gc 观察 S0U/S1U 在 GC 后是否长期 >70%;持续高位说明 Survivor 已逼近临界,动态晋升正在高频发生
本文共计942个文字,预计阅读时间需要4分钟。
动态对象年龄决定不单纯是存活几轮就走,而是看+Survivor+区域内某一年龄段的对象是否已将近填满。只要这一年龄段的对象占总大小+Survivor+总容量的一半(默认50%),则该年龄段的对象在下一次Minor GC后直接进入老年代。
晋升触发的关键条件
这个机制不跨年龄累加,也不看平均或预测,只做一次硬性扫描和判断:
- Minor GC 结束后,JVM 扫描当前 Survivor 区中所有存活对象,按年龄分组统计大小
- 从 age=1 开始,逐档累加:age1 + age2 + … 直到累计值首次 ≥ Survivor 容量 × TargetSurvivorRatio(默认 0.5)
- 此时的年龄就是新阈值,比如累加到 age3 达标,则 age3 及以上全部晋升
- 注意:必须是“某一个具体年龄档”的累计值达标,不是 age1 占 40%、age2 占 20% 合起来算——那是错误理解
GC 日志怎么看实际晋升年龄
打开 -XX:+PrintGCDetails 后,Minor GC 日志中会出现类似这行:
Desired survivor size 524288 bytes, new threshold 3 (max 6)
其中 new threshold 3 就是本次动态计算出的晋升起点年龄。它每轮 GC 都可能变:
- 如果长期显示 new threshold 6 (max 6),说明 Survivor 空间宽松,没触发动态逻辑
- 如果频繁出现 new threshold 1 或 2,大概率是 Survivor 过小,或某批对象生命周期高度一致(如批量请求生成的 VO)
- 日志中还会打印各年龄对象明细,例如:age 1: 262144 bytes, 262144 total —— 这表示 age1 对象共 256KB,若 Survivor 是 512KB,就已超半,直接触发 age1 晋升
为什么不是“所有 age≥X 的对象都该走”?要结合空间压力
这个机制本质是空间保底策略:防止 Survivor 区被大龄对象“卡死”,导致后续小对象无处可放。它的出发点不是对象该不该老,而是“Survivor 还能不能用”。所以:
- 即使你设了 -XX:MaxTenuringThreshold=15,只要 age2 对象占满 Survivor 一半,JVM 就会无视 15,强制 age2+ 晋升
- 如果 Survivor 区本身太小(比如 -XX:SurvivorRatio=2),age1 对象稍多一点就容易超半,导致大量新生对象“一岁就进老年代”
- 这种晋升和担保失败不同:它不依赖空间是否溢出,而是在复制前就根据分布预判;担保失败则是 To 区真装不下,属于兜底行为
调优时要注意的配合点
单改 MaxTenuringThreshold 基本无效,必须和 Survivor 空间配置联动:
- -XX:TargetSurvivorRatio 控制判定比例,默认 50%,可微调(如设为 60% 可减少误触,但需确保 Survivor 有余量)
- -XX:SurvivorRatio 决定 Survivor 大小,建议从默认 8 放宽到 6 或 4(即 Eden:Survivor = 6:2),让每个 Survivor 占年轻代 1/4,留出缓冲
- 必须启用 -XX:+UseAdaptiveSizePolicy(默认开启),否则整个动态机制不生效,退化为固定阈值
- 监控关键指标:用 jstat -gc 观察 S0U/S1U 在 GC 后是否长期 >70%;持续高位说明 Survivor 已逼近临界,动态晋升正在高频发生

