1.JDK VM.Thread里的 Safepoint机制
| JDK VM.Thread 各种循环与Safepoint GC STW 相关问题 专题文章列表 |
|---|
| 1.JDK VM.Thread里的 Safepoint机制 |
| 2.JVM JIT区分非可数循环与可数循环 |
| 3.JVM 区分非可数循环与可数循环 JIT编译器省略Safepoint检查,导致该线程无法进入Safepoint,而阻塞VM Thread和其他所有线程 |
| 4.调整JVM参数或规范编码规避GC判定线程无Safepoint导致的STW问题 |
1.JDK VM.Thread里的 Safepoint机制
在 JDK 8 中,VM Thread 是 JVM 内部的一个重要线程,负责执行各种“VM 操作”(VM Operations),比如垃圾回收(GC)、类加载、线程堆栈 dump、偏向锁撤销等。这些操作通常需要“全局一致性”状态,因此必须在所有 Java 线程到达一个安全点(Safepoint)时才能执行。
一、什么是 Safepoint(安全点)?
Safepoint 是 JVM 中的一个机制,用来确保所有 Java 线程在执行某些 VM 操作前,都处于一个“可被安全暂停”的状态。
在 Safepoint,每个线程的执行状态是已知的,JVM 可以安全地操作堆、元空间、线程栈等,而不会破坏对象图或导致数据不一致。
二、为什么需要 Safepoint?
JVM 的很多操作(如 GC)需要“Stop-The-World”(STW),即暂停所有 Java 线程。但不能在任意位置暂停线程,否则可能:
- 正在修改对象引用,导致对象图不一致
- 执行到一半的字节码指令,状态不完整
- 锁状态不一致
因此,JVM 会等待所有线程到达“安全点”后再执行这些操作。
三、VM Thread 与 Safepoint 的关系
VM Thread 是执行这些需要 Safepoint 的操作的线程。它不会主动执行 GC 或其他任务,而是从“VM Operation Queue”中取出任务,并在所有线程到达 Safepoint 后执行。
VM Thread 的典型行为:
- 收到一个 VM 操作请求(如
VM_GC_Operation) - 请求进入 VM Operation 队列
- VM Thread 开始处理该请求
- 触发 Safepoint 检查:通知所有 Java 线程进入 Safepoint
- 等待所有线程响应并到达 Safepoint
- 执行 VM 操作(如 GC)
- 恢复所有线程运行
四、Safepoint 是如何实现的?(JDK 8)
在 JDK 8 中,Safepoint 的实现基于“轮询机制”:
1. Safepoint Polling(轮询)
JVM 在生成的代码中插入Safepoint 检查点(通常是循环末尾、方法返回前、方法调用前等):
test %rax, %rax
jne <safepoint_handler>
这行汇编代码实际上是在检查一个全局的“Safepoint 标志”(Poll_Page 或 Safepoint Poll)。如果 JVM 设置了该标志(表示需要进入 Safepoint),线程就会跳转到 Safepoint 处理逻辑。
这个标志通常通过“内存页保护”或“内存映射页”来实现。例如:让某个内存页不可读,当线程访问它时触发
SIGSEGV,JVM 捕获信号并处理为进入 Safepoint。
2. Safepoint 的触发方式
- GC 触发
- biased lock revocation
- 线程 dump
- JIT 去优化(deoptimization)
- Class 操作(如 redefine class)
3. 线程如何进入 Safepoint?
- 解释执行的线程:在字节码解释器中定期检查
- JIT 编译的代码:在生成的机器码中插入 Safepoint Poll
- Native 线程:不能立即进入 Safepoint,必须等到它们返回 Java 代码(
_thread_in_native→_thread_in_Java)
Native 线程无法被强制中断,因此可能造成 Safepoint 延迟(常见性能问题)
五、如何监控 Safepoint?
JDK 8 提供了以下 JVM 参数来监控 Safepoint:
-XX:+PrintSafepointStatistics
-XX:+PrintGCApplicationStoppedTime
-XX:+PrintGCApplicationConcurrentTime
-XX:+UnlockDiagnosticVMOptions
输出示例:
VM Operation [GC pause (G1 Evacuation Pause)]
Safepoint "GC", Safepoint::do_cleanup_tasks()
Total time for which application threads were stopped: 0.0123456 secs
你还可以通过:
jcmd <pid> VM.safepoint
查看当前 Safepoint 状态。
六、Safepoint 常见问题
1. Safepoint 迟迟无法进入
原因:
- 有线程长时间运行在 Native 状态(如 JNI 调用、sleep、IO)
- 线程在执行没有 Safepoint Poll 的长循环(JIT 优化后可能移除 Poll)
解决:
- 使用
-XX:GuaranteedSafepointInterval=300000(每 5 分钟强制 Safepoint) - 避免长时间 JNI 调用
- 使用
jstack查看哪些线程未进入 Safepoint
2. STW 时间过长
可能是因为:
- GC 停顿
- 偏向锁批量撤销(Biased Lock Revocation)
- JIT 去优化
七、Safepoint 与 Stop-The-World(STW)
- 所有 Safepoint 操作都会导致 STW
- STW 时间 = 等待所有线程进入 Safepoint 的时间 + VM 操作执行时间
- 有时“进入 Safepoint”的时间比 GC 本身还长(特别是有线程卡在 Native)
总结
| 概念 | 说明 |
|---|---|
| VM Thread | 执行 VM 操作的后台线程 |
| Safepoint | 所有线程必须到达的安全位置,用于执行 STW 操作 |
| Safepoint Polling | 在代码中插入检查,判断是否需要进入 Safepoint |
| 进入条件 | 循环、方法调用、异常处理等位置 |
| Native 线程 | 无法立即响应,可能导致 Safepoint 延迟 |
| 监控工具 | jcmd, -XX:+PrintSafepointStatistics, GC 日志 |
哪些版本JDK有VM Thread
几乎所有现代版本的 JDK(包括 JDK 8、JDK 11、JDK 17、JDK 21 等)都包含 VM Thread,它是 HotSpot JVM 的核心组成部分之一,用于执行需要全局一致性的“VM 操作”。
✅ 八、VM Thread 的存在范围
| JDK 版本 | 是否有 VM Thread | 说明 |
|---|---|---|
| JDK 8 | ✅ 是 | 经典的 VM Thread 实现 |
| JDK 9 | ✅ 是 | 模块化,但 VM Thread 保留 |
| JDK 11 | ✅ 是 | 长期支持版(LTS),VM Thread 仍在 |
| JDK 17 | ✅ 是 | LTS 版本,VM Thread 依然存在 |
| JDK 21 | ✅ 是 | 最新 LTS,VM Thread 仍然关键 |
| OpenJDK / Oracle JDK | ✅ 都有 | 所有基于 HotSpot 的 JVM 都有 |
🔹 结论:只要使用的是 HotSpot JVM,无论哪个 JDK 版本,都会有
VM Thread。
✅ 九、VM Thread 的作用在各版本中基本一致
VM Thread 的职责在 JDK 8 到 JDK 21 中基本保持不变,主要负责:
| 功能 | 说明 |
|---|---|
| 执行 GC 操作 | 如 G1、ZGC(部分阶段)、CMS、Parallel GC 的 STW 阶段 |
| 偏向锁撤销(Biased Lock Revocation) | JDK 15+ 默认禁用,但仍支持 |
| 类重定义(JVM TI, redefine classes) | 调试、热更新(如 HotSwap) |
| 线程 dump / 堆 dump | jstack, jmap 等工具触发的操作 |
| 去优化(Deoptimization) | JIT 回退到解释执行 |
| Safepoint 协调 | 等待所有线程进入 Safepoint |
✅ 十、不同 JDK 版本中的变化(VM Thread 的演进)
虽然 VM Thread 一直存在,但随着 JVM 的演进,它的使用频率和重要性有所下降,尤其是在低延迟 GC 出现后。
1. JDK 11+:ZGC 和 Shenandoah 的引入
- ZGC(JDK 11+ 实验,JDK 15+ 生产可用)
- Shenandoah(JDK 12+)
- 这些 GC 的 大部分操作是并发的,不再依赖 VM Thread 执行 STW 操作
- 但 某些操作仍需 VM Thread,如:
- 初始标记(Initial Mark)
- 最终标记(Final Mark)
- 引用处理
- 类卸载
⚠️ 即使是 ZGC,仍然需要 Safepoint 和 VM Thread 来执行这些短暂停顿操作
2. JDK 15:默认禁用偏向锁(Biased Locking)
- 偏向锁撤销是 VM Thread 的重负载操作
- JDK 15 起默认禁用(
-XX:-UseBiasedLocking) - 减少了 VM Thread 的工作量,降低 Safepoint 延迟
3. JDK 18:删除永久代(PermGen),元空间(Metaspace)
- 类加载/卸载机制变化,但 VM Thread 仍参与类元数据清理
4. JDK 21:虚拟线程(Virtual Threads)
- 虚拟线程(Project Loom)大量使用
Carrier Thread(平台线程) - 但 VM Thread 仍然存在,用于底层 VM 操作
- 虚拟线程不会改变 Safepoint 和 VM Thread 的底层机制
✅ 十一、如何验证 VM Thread 存在?
你可以通过以下方式查看:
1. 使用 jstack 查看线程
jstack <pid> | grep "VM Thread"
输出示例:
"VM Thread" os_prio=0 tid=0x00007f8c8c00a000 nid=0x1a4b runnable
2. 使用 jcmd 查看 Safepoint
jcmd <pid> VM.safepoint
3. 启用 Safepoint 日志
-XX:+PrintSafepointStatistics -XX:+PrintGCApplicationStoppedTime
✅ 十二、有没有 JVM 没有 VM Thread?
极少数非 HotSpot 的 JVM 实现可能没有“单一 VM Thread”,例如:
| JVM 实现 | 是否有 VM Thread |
|---|---|
| HotSpot (Oracle/OpenJDK) | ✅ 有 |
| OpenJ9 (IBM) | ❌ 无(使用不同机制) |
| GraalVM Native Image | ❌ 无(AOT 编译,无运行时 VM Thread) |
| Azul Zing | ✅ 有,但实现不同(C4 GC 并发性强) |
🔹 所以:VM Thread 是 HotSpot 特有的设计,不是所有 JVM 都有。
✅ 总结
| 问题 | 回答 |
|---|---|
| JDK 8 之后还有 VM Thread 吗? | ✅ 有,一直到 JDK 21 都存在 |
| VM Thread 的作用变了吗? | 基本不变,但使用频率降低(因并发 GC) |
| ZGC / 虚拟线程会取代 VM Thread 吗? | ❌ 不会,它仍是底层基础设施 |
| 哪些 JVM 没有 VM Thread? | OpenJ9、GraalVM Native Image 等非 HotSpot 实现 |

浙公网安备 33010602011771号