jstat -gcutil 不显示晋升量是因为只输出各区域使用率百分比,晋升量需通过jstat -gc中ou增量减fgc回收量推算;可结合e、ou斜率及s0u/s1u波动识别异常晋升。

直接看 YGC 和 OU 的变化斜率,再结合 EC、S0U、S1U 的波动,就能判断晋升是否异常——jstat -gcutil 本身不提供“晋升量”字段,必须靠推算。
为什么 jstat -gcutil 没有直接显示晋升量
jstat -gcutil 只输出各区域的使用率百分比(如 E 表示 Eden 使用率,O 表示老年代使用率),不暴露每次 YGC 后有多少字节/对象进入老年代。真正的晋升数据藏在 jstat -gc 的原始值里:OU(老年代已用)的增量减去老年代自身触发的 FGC 回收量,才是 YGC 带来的净晋升。
- 常见误判:看到
O从 35% 突增到 42%,就认为“这次 YGC 晋升了 7%”,但实际可能是前一次 YGC 晋升的对象刚被 FGC 清掉一部分,导致OU下降,后续又补上——百分比掩盖了绝对量波动 - 关键限制:JDK 8+ 的
jstat -gcutil输出中没有YGCT对应的“晋升字节数”列,这是设计使然,不是参数漏配 - 替代方案:若需精确晋升量,必须用
jstat -gc -h10 PID 1000(每秒刷新、带表头),然后人工或脚本计算OU的差值,并排除FGC干扰
如何用 jstat -gcutil 快速识别异常晋升
虽然不能读出具体字节数,但通过三组指标联动观察,能高效定位晋升风暴:
- 盯住
E(Eden 使用率)和YGC频次:若E在 90%~99% 区间频繁跳变(如 2 秒内从 10% → 95% → 20%),且YGC计数猛增,说明 Eden 频繁打满 → 晋升压力前置 - 对比
S0/S1使用率(S0U/S0C、S1U/S1C)与O趋势:正常时 Survivor 区应有明显“一空一满”交替;若两个 Survivor 都长期 >60% 且O同步爬升,大概率触发了动态年龄晋升(Survivor 空间不足,提前把年龄未达MaxTenuringThreshold的对象送入老年代) - 检查
YGC后O是否“阶梯式上升”:连续几次 YGC 后O稳定涨 1%~2%,属合理;若某次 YGC 后O突增 5%+ 且无 FGC,基本可断定有大对象(>-XX:PretenureSizeThreshold)直入老年代,或 Survivor 区过小被迫晋升
jstat -gcutil 的刷新节奏与采样陷阱
默认单次执行只输出当前快照,无法反映趋势。必须配合时间间隔和重复次数才能捕获动态:
- 正确命令:
jstat -gcutil PID 1000 5(每秒刷新,共 5 次),观察O列是否呈现“锯齿上升”而非平缓增长 - 致命误区:用
jstat -gcutil PID单次执行,看到O=41.2%就下结论“老年代快满了”,却忽略此时OC(老年代容量)可能高达 4GB,而OU实际才 1.6GB——百分比脱离绝对值毫无意义 - 兼容性注意:JDK 8 与 JDK 11+ 的
jstat -gcutil字段顺序一致,但 JDK 17 开始部分云厂商定制版可能隐藏CCSU等元空间字段,不影响核心判断 - 性能开销:该命令对 JVM 几乎零侵入,生产环境可放心高频采样(≤100ms 间隔)
真正难的是区分“合理晋升”和“病态晋升”:前者是对象自然老化,后者往往伴随 Survivor 区反复溢出、YGC 耗时陡增、或 OU 增量远超 EU 回收量。盯住 S0U、S1U、EU、OU 四个原始值的变化关系,比任何百分比都可靠。











