欢迎光临
我们一直在努力

详解 JVM 停顿现象:如何排查除了 GC 以外引发的 SafePoint 安全点停顿

JVM的Safepoint(安全点)停顿是保障JVM内部操作(如垃圾回收、JIT编译优化或去优化、偏向锁撤销等)正确执行的关键机制。当JVM需要进行这些“世界级”的操作时,它必须确保所有Java线程都停止在一个稳定且已知的状态,即Safepoint。虽然GC是Safepoint最常见的触发者,但在高性能应用中,非GC原因导致的Safepoint停顿也可能是严重的性能瓶颈。

本文将深入探讨非GC原因的Safepoint停顿,并提供实操性的排查方法。

1. 什么是Safepoint停顿?

Safepoint是一种机制,它要求所有执行Java代码的线程必须停下来,等待JVM完成内部操作。JVM通过在循环体末尾和方法返回前等位置插入“安全点检查”(Safepoint Polling)指令来实现这一目的。当Safepoint请求发出时,线程会在下一次检查时发现请求并暂停执行。

线程从请求Safepoint到所有线程实际停止的这段时间被称为“Safepoint Time”或“STW Delay”,这是性能分析中需要关注的核心指标。

2. 常见的非GC Safepoint触发原因

除了GC之外,以下几个操作是导致Safepoint停顿的常见元凶:

  1. 偏向锁撤销 (Biased Locking Revocation): 当对象上的偏向锁竞争激烈,或者偏向锁需要升级为轻量级/重量级锁时,JVM需要撤销偏向锁的状态。这个操作必须在Safepoint下进行,以保证对象头信息的原子性修改。
  2. JIT 编译/去优化 (Deoptimization): JIT编译器可能会因为内部原因(如代码热度变化、方法内联失败等)将已编译的代码恢复到解释执行状态。这种批量去优化操作(Bulk Deoptimization)需要STW。
  3. JVMTI 操作: 外部工具(如Profiler、Debugger、或者执行jstack获取线程转储)通过 Java 虚拟机工具接口 (JVMTI) 请求获取全局状态时,通常需要JVM进入Safepoint。
  4. Class Redefinition / HotSwap: 类的重新定义或热加载。

3. 如何开启 Safepoint 详细日志

要诊断非GC Safepoint问题,最直接的方法是启用详细的Safepoint统计日志。我们需要使用以下关键的JVM启动参数:

# 核心诊断参数
-XX:+PrintSafepointStatistics \
-XX:PrintSafepointStatisticsCount=1 \
-XX:+PrintGCDetails
  • PrintSafepointStatistics: 启用Safepoint日志输出。
  • PrintSafepointStatisticsCount=1: 确保每个Safepoint事件都单独输出一行统计信息。

4. 日志解读:识别非GC停顿

在JVM输出的日志中,Safepoint事件通常被记录在一个表格结构中。关键指标包括:

字段 含义
Time JVM启动至今的时间 (秒)
Total time 本次 Safepoint 总耗时 (ms)
VM Operation 触发 Safepoint 的操作类型
Sync Time 等待所有 Java 线程到达 Safepoint 的时间 (ms)

示例日志片段分析

如果日志中显示长时间停顿,但VM Operation不是以GC开头,那么就找到了非GC问题。

示例 1:Biased Locking Revocation (偏向锁撤销)

Safepoint synchronization time: 0.005 ms
[ 43.123s ][ info ][ safepoint ] Total time: 10.321 ms, Reaching safepoint: 9.876 ms, At safepoint: 0.445 ms, VM Operation: BulkRevokeBias

在这个例子中:
* VM Operation: BulkRevokeBias 表明是批量偏向锁撤销操作。
* 总耗时 10.321ms,其中大部分时间 (9.876ms) 都花在了等待线程到达 Safepoint 上(Reaching safepoint,即 Sync Time)。

解决思路: 如果发现大量的 BulkRevokeBias 导致停顿,且应用场景确实存在大量多线程竞争同一对象的锁,可以考虑关闭偏向锁优化来避免频繁撤销,参数为:-XX:-UseBiasedLocking

示例 2:JIT Deoptimization (批量去优化)

Safepoint synchronization time: 0.004 ms
[ 102.500s ][ info ][ safepoint ] Total time: 5.112 ms, Reaching safepoint: 4.900 ms, At safepoint: 0.212 ms, VM Operation: BulkDeoptimize
  • VM Operation: BulkDeoptimize 表明发生了批量去优化。这通常是由于代码依赖的假设条件被打破(例如,内联方法中的类结构发生变化,或计数器溢出)。

解决思路: 排查应用启动初期或负载变化时是否有大量的类加载或反射操作,这些操作可能会导致JIT代码失效。如果去优化是系统运行稳定后的周期性行为,则可能需要更深入地分析JIT编译器日志(如-XX:+UnlockDiagnosticVMOptions -XX:+PrintCompilation)。

5. 总结

Safepoint 停顿是 JVM 性能调优中容易被忽略的细节。通过开启详细的Safepoint日志并关注VM OperationReaching safepoint (同步等待) 时间,我们可以快速定位到是 GC 还是像偏向锁撤销、JIT去优化等非 GC 操作导致了应用线程的停滞,从而进行针对性优化。

【本站文章皆为原创,未经允许不得转载】:汤不热吧 » 详解 JVM 停顿现象:如何排查除了 GC 以外引发的 SafePoint 安全点停顿
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址