GC停顿(STW)是JVM通过SafePoint机制让所有Java线程在状态一致的位置主动挂起,而非强制中断;线程仅在SafePoint检查全局标志并自愿暂停,以确保可达性分析的准确性与安全性。

GC 停顿(STW)不是 GC 自己“按下暂停键”,而是 JVM 依赖 SafePoint 机制,让所有 Java 线程在可预测、状态一致的位置主动挂起。关键不在于“强制中断”,而在于“约定同步”——线程只在安全点检查是否该停,且只在安全点才能保证栈和寄存器中的对象引用关系是确定、可枚举的。
SafePoint 是什么:不是位置,而是状态契约
SafePoint 不是代码行号或字节码索引,而是一组被 JVM 标记的执行点。在这些点上:
- 线程的执行上下文(如局部变量表、操作数栈)中所有对象引用都已固化,能被准确映射为 OopMap
- 不会正在修改对象图(比如刚 new 出对象但还没完成字段赋值)
- JVM 可以安全地执行可达性分析,无需担心引用被并发变更
为什么选这些地方设 SafePoint?
SafePoint 的分布兼顾“及时性”和“低开销”,主要出现在天然具备较长执行窗口的指令处:
- 方法调用返回前(call/return 指令)
- 循环体末尾(尤其是 for/while 的跳转回起点处)
- 异常抛出与处理边界(throw / catch 入口)
- 某些 JVM 内部 safepoint poll 插入点(如反优化入口、锁膨胀后)
这些位置本身执行耗时相对可观,插入轮询检查几乎不增加额外延迟;而高频指令(如 iadd、iload)则避开,避免性能损耗。
线程怎么“响应”停顿请求?靠轮询 + 标志位
JVM 不会强行打断正在运行的线程。它采用主动式中断(Voluntary Suspension):
- GC 触发时,JVM 设置全局标志(如 VMThread 写入 needs_gc)
- 每个 Java 线程在进入 SafePoint 时,自动执行一次 poll 检查(例如通过 test %rax, [poll_page] 类似汇编)
- 若标志为真,线程立即转入 blocked 状态,加入 safepoint barrier 等待队列
- 所有线程就绪后,GC 才正式开始枚举 GC Roots 和清理内存
线程睡着了、锁住了怎么办?SafeRegion 来兜底
当线程处于 sleep、wait、blocked on monitor 或执行 native 代码时,无法主动走到 SafePoint。这时启用 SafeRegion:
- 一段代码逻辑内,确认不会发生任何引用关系变更(如纯计算、IO 等待前)
- 线程进入该区域时,标记自己为 in_safe_region = true
- JVM 发起 GC 时,直接跳过这些线程,不等它们“醒来”
- 线程退出 SafeRegion 前,检查 GC 是否结束;未结束则阻塞等待
这相当于把“必须停在某一点”的硬约束,扩展为“停在某一片安全区间内”,覆盖了非运行态线程的同步盲区。

















