maxtenuringthreshold 控制对象在 survivor 区经历多少次 minor gc 后晋升老年代,需结合 survivor 空间大小与实际晋升日志调优,不同 gc 回收器对其依赖程度不同,盲目设高或设低均不可取。

MaxTenuringThreshold 控制对象在 Survivor 区经历多少次 Minor GC 后晋升到老年代,默认值通常是 15(CMS 和 G1 是 6,ZGC/Shenandoah 不使用该阈值)。调优目标不是盲目设高或设低,而是让对象晋升时机匹配其真实存活周期——短命对象及时清理,长命对象尽早进入老年代,避免 Survivor 区反复复制和过早晋升带来的浮动垃圾或 Promotion Failure。
看实际晋升行为,而不是凭经验设值
开启 GC 日志(如 -Xlog:gc*,gc+age=debug)后,重点关注每次 Minor GC 后各年龄对象的大小分布。如果日志中大量 age=1、age=2 的对象就进入了老年代,说明阈值设得太高或 Survivor 空间太小;如果 age=15 的对象才开始晋升,而堆中又存在大量长期存活对象,说明阈值可能偏高,浪费了 Survivor 复制开销。
- 用 jstat -gc
观察 S0C/S1C 使用率和 YGC 后 Survivor 区存活对象占比 - 结合 -XX:+PrintTenuringDistribution 查看每次 GC 后各年龄对象累计大小
- 若发现 age=1 就晋升的对象占比持续 >20%,优先检查 SurvivorRatio 或直接降低 MaxTenuringThreshold
配合 Survivor 空间大小一起调
MaxTenuringThreshold 的效果高度依赖 Survivor 区是否足够容纳“该活”的对象。Survivor 太小会导致对象被迫提前晋升,哪怕年龄远未达标;太大又可能让短命对象滞留过久,增加复制成本。
- 通过 -XX:SurvivorRatio=N 调整 Eden:Survivor 比例(默认 8,即 Survivor 占年轻代 1/10)
- 当观察到 Survivor 区经常在 GC 后接近 100% 使用率,且 age=1 对象大量晋升,可尝试增大 Survivor(减小 SurvivorRatio)或同步降低 MaxTenuringThreshold
- 例如:把 SurvivorRatio 从 8 改为 4(Survivor 翻倍),再将 MaxTenuringThreshold 从 15 降到 4~6,常能显著减少老年代压力
不同回收器的行为差异必须考虑
并非所有 GC 都严格遵循 MaxTenuringThreshold。它的实际作用因回收器而异:
- Parallel GC / Serial GC:严格按年龄阈值晋升,是调优主战场
- CMS:默认阈值为 6,且启用 -XX:+UseCMSCompactAtFullCollection 时可能忽略年龄直接晋升
- G1:基本不看这个参数,靠 -XX:G1MixedGCCountTarget 和对象活跃度决定何时晋升;MaxTenuringThreshold 仅在初始混合 GC 阶段有弱影响
- ZGC / Shenandoah:无传统分代概念,该参数被忽略
避免常见误操作
调优不是“设一个固定值就一劳永逸”,更不是越小越好或越大越好:
- 不要把 MaxTenuringThreshold 设为 0 —— 这会让所有对象在第一次 Minor GC 后就进老年代,彻底废掉年轻代设计
- 不要脱离 GC 日志凭感觉调;一次只改一个参数(如先调 SurvivorRatio,稳定后再微调阈值)
- 高并发短事务服务(如 API 网关)通常适合较低阈值(如 2~4),而批处理类长流程应用可适当提高(6~8)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











