JVM GC 日志中 STW 时间由 real time 字段直接体现:旧版为“secs”、新版为“ms”,ZGC/Shenandoah 中带“Pause”的行也需计入;user/sys 仅为 CPU 耗时,非停顿时间;须汇总所有 STW 事件并关注非 GC 类停顿。

应用线程暂停时间(即 STW 时间)在 JVM GC 日志中不是靠估算,而是直接由日志中明确标出的 real time 字段体现——它代表所有应用线程被强制挂起的真实时长。
看准日志里的 “secs” 或 “ms” 数值
每次 GC 记录末尾的耗时数字就是关键:
-
旧版参数(JDK 8 及以前):如
[GC (Allocation Failure) ... 0.0421234 secs],0.0421234 secs = 42.1 ms 就是本次 STW 时间 -
新版参数(JDK 9+,-Xlog):如
[2025-04-05T10:12:33.124+0800][info][gc,pause] GC(12) Pause Young 42.123ms,末尾的 42.123ms 即为实测停顿 - ZGC/Shenandoah 日志中带
Pause标签的行(如Pause Initial Mark 1.2ms)也属有效 STW 阶段,需全部纳入统计
别把 user/sys 当成停顿时间
user 是 GC 线程在用户态消耗的 CPU 时间,sys 是内核态时间;二者之和反映 JVM 内部计算开销,但不等于线程暂停时长。真正影响业务响应的是 real(即 wall-clock time):
- 若
real ≈ user + sys:说明停顿基本纯属 GC 工作本身,无明显外部等待 - 若
real 远大于 user + sys(比如 real=120ms,user+sys=15ms):说明线程卡在安全点等待、锁竞争、OS 调度延迟等非 GC 原因,需结合-XX:+PrintSafepointStatistics进一步排查
汇总所有 STW 事件才算准总停顿
单次停顿短不代表没压力。高频小停顿会显著拖垮吞吐量,必须累计所有 STW 时间:
- 用脚本提取全部含
secs、ms、Pause的行,转换单位后求和(推荐用awk或 Python 快速处理) - 工具更省事:上传日志到 GCeasy 或 GCViewer,它们自动识别所有 STW 阶段,输出 Total Pause Time 和 Pause Time %
- 健康参考线:Web 类服务,10 分钟内总停顿超 18 秒(即占比 >3%) 就需干预;批处理类可放宽至 8%
注意隐藏的非 GC 类停顿
有些停顿不在 GC 日志里显示为 “GC”,但同样导致应用线程冻结:
- 启用
-XX:+PrintGCApplicationStoppedTime,它会单独打印每次 JVM 全局暂停(无论是否由 GC 引起) - 配合
-XX:+PrintSafepointStatistics查看线程是否因长时间计算、IO、native 调用无法进入安全点 - Metaspace 扩容失败、CodeCache 耗尽、JIT 编译阻塞等也会触发 STW,日志中表现为
Metadata GC Threshold或CodeCache is full

















