STW是保障内存操作正确性的必要手段,因可达性分析需基于静止一致的内存快照,否则引用关系动态变化会导致漏标或错标;根节点枚举必须在STW下完成,所有现代JVM均遵循此前提。

STW 不是 JVM 的“bug”,而是保障内存操作正确性的必要手段。核心在于:垃圾回收必须基于一个静止、一致的内存视图,否则标记结果不可信。
为什么必须冻结线程才能做可达性分析
可达性分析依赖 GC Roots(如栈中局部变量、静态字段、JNI 引用等)出发扫描存活对象。如果用户线程在扫描过程中持续修改引用关系——比如把某个对象从 A 变量移到 B 变量、或把引用置为 null——那么 GC 线程看到的就可能是“中间态”:同一对象在不同时间点被判定为存活或死亡,导致漏标(该活的被回收)或错标(该死的被保留)。
这种不一致会直接破坏 Java 内存模型的安全性,轻则引发 NullPointerException 或诡异的数据丢失,重则造成堆损坏。因此 JVM 必须让所有用户线程暂停,使整个堆和栈的状态“定格”在一个时间点上,形成一致性快照。
根节点枚举阶段为何无法并发执行
GC Roots 分布在 Java 栈、本地方法栈、方法区静态字段、JNI 引用表等多个地方,它们本身是动态变化的。例如,一个方法刚压入栈帧,其局部变量表就新增了若干可能指向堆对象的引用;另一个方法刚返回,这些引用又立刻失效。
若不暂停线程,GC 就无法准确捕获“此刻全部有效的 Roots”。哪怕只差几纳秒,就可能漏掉一个正在被使用的对象——而这个对象一旦被错误回收,程序就会崩溃。所以根枚举必须在 STW 下完成,这是所有现代 JVM 垃圾回收器的共同前提。
安全点(SafePoint)是 STW 的实际落脚点
JVM 并不会在任意指令处中断线程,而是在预设的安全点位置等待线程到达。这些位置通常是方法调用返回、循环回边、异常抛出等指令附近——此时线程的栈帧结构稳定,寄存器中的对象引用状态可被精确捕获。
- 线程运行到安全点后主动挂起,JVM 才能开始 GC 或其他全局操作
- 如果某个线程长时间处于计算密集型循环中(且未设置回边检查),它可能延迟到达安全点,拉长 STW 实际等待时间
- 可通过 -XX:+PrintSafepointStatistics 查看安全点停顿分布,辅助定位长停顿诱因
不只是 GC,还有哪些操作依赖 STW
STW 是 JVM 提供全局同步能力的基础设施,并非仅为垃圾回收服务:
- JIT 反优化:当热点代码被发现不再适用(如类型假设失败),需退回到解释执行,此时要确保所有线程已退出该方法栈帧
- 偏向锁撤销:多个线程竞争同一把锁时,需暂停持有偏向锁的线程,将其锁升级为轻量级锁
- 线程堆栈转储(jstack):获取完整、一致的线程快照,避免栈帧处于中间状态
- 类重定义(热更新):替换字节码前,必须确保没有线程正执行旧版本方法

















