-XX:+PrintGCApplicationStoppedTime 专门输出所有Stop-the-World事件的真实暂停时长(含GC及safepoint等非GC停顿),精度达微秒级;而-XX:+PrintGCDetails仅记录GC各阶段耗时,不反映实际STW时长。

-XX:+PrintGCApplicationStoppedTime 是 JVM 提供的一个轻量级、低开销的日志参数,专门用于打印每次 Stop-the-World(STW)事件的精确暂停时长,不依赖 GC 类型,也不要求开启详细 GC 日志。它输出的是 JVM 实际让所有 Java 应用线程(Mutator)停顿的真实时间,包括 GC 引起的 STW,也包括 safepoint 进入/退出、类加载、JIT 编译、偏向锁撤销等非 GC 类型的全局停顿。
它和 -XX:+PrintGCDetails 的区别在哪?
– PrintGCDetails 只记录 GC 活动本身的日志(如 Eden 复制耗时、并发标记阶段耗时),但不体现 STW 实际持续多久——比如 G1 的 Evacuation 阶段标称耗时 12ms,但真实 STW 可能是 15ms(含 safepoint 等待);
– PrintGCApplicationStoppedTime 单独一行输出:Application time: 0.0003420 seconds(这是应用运行时间),紧接着就是:Total time for which application threads were stopped: 0.0152870 seconds——这个值才是你该关注的“真实世界停顿”,毫秒级精度,可信度高。
如何启用并正确解读输出?
在 JVM 启动参数中加入:
-XX:+PrintGCApplicationStoppedTime -Xloggc:gc_stw.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=10M
注意:
• 不需要搭配 -XX:+PrintGCDetails 或 -Xlog 就能独立工作;
• 输出默认到 stdout,建议重定向或配合 -Xloggc 写入文件便于分析;
• 每次 STW 都会输出一行 Total time for which application threads were stopped: X.XXXXXXX seconds,单位是秒,保留 7 位小数,足够定位亚毫秒级抖动。
哪些场景下它特别有用?
- 排查“明明没发生 GC,但接口 P99 突然毛刺”:可能源于大量线程竞争 safepoint,或频繁去偏锁(
-XX:+PrintBiasedLockingStatistics可辅助验证); - 验证 G1 的 -XX:MaxGCPauseMillis 是否达标:对比日志中 STW 时间是否稳定低于设定阈值,而非只看 GC 日志里的“predicted pause”;
- 识别非 GC 类停顿:例如动态 Attach Agent、JFR 控制指令、JMX full thread dump 触发的全局暂停,这些都会被该参数捕获;
- 压测中做 STW 基线比对:开启前后分别采集 5 分钟日志,用 awk/grep 统计平均值、P95、最大值,快速定位优化效果。
使用时要注意什么?
• 它本身几乎无性能损耗,但高频 STW(如每秒数十次)会导致日志量激增,建议生产环境仅在问题复现期短期开启;
• 输出不含上下文(比如哪次 STW 是由 CMS Init Mark 引起,还是由 JIT deopt 引起),需结合 -XX:+PrintSafepointStatistics -XX:PrintSafepointStatisticsCount=1 进一步定位原因;
• 如果看到大量 sub-millisecond 级 STW(如 0.000123s),大概率是 safepoint 抢占等待,说明应用线程长时间未到达安全点(比如在执行大循环、Unsafe.getXXX、JNI 长调用);
• 注意区分 “Application time” 和 “stopped time”:前者是两次 STW 之间的应用运行时间,可用于反推吞吐率,但不是 CPU 时间。

















