问题
在需要进行标记、压缩、清理时,JVM 是如何停止 Java 线程以进行STW的?
基础知识
假设您拥有像 JVM 这样的托管运行时,并且您需要偶尔停止 Java 线程以运行一些运行时代码。例如,您想要执行STW的 GC。您可以等待所有线程最终调用 JVM,例如,请求分配(通常是TLAB重新填充),或输入一些本地方法(转换为本地会捕获它),或执行其他操作。但这并不能保证会发生!如果线程当前正在某种忙循环中运行,从未执行任何特殊操作怎么办?
在大多数机器上,停止正在运行的线程其实很简单:您可以向它发送信号、强制处理器中断等,使其停止线程正在执行的操作并将控制权转移到其他地方。但是,Java 线程在任意点停止通常是不够的,特别是当您想要精确的垃圾收集时。在那里,您想知道寄存器和堆栈中有什么,以防这些值实际上是您需要处理的对象引用。或者,如果您想取消锁定,您需要获得有关线程状态和获取的锁定的精确信息。或者,如果您对方法进行反优化,您确实希望从安全位置执行此操作,而不会丢失已执行的代码部分和/或临时值。
因此,现代 JVM(如 Hotspot)实现了协作方案:线程在其生命周期的某些已知点(当其状态已知时)不时询问是否应将控制权移交给 VM。当所有线程都停止在这些已知点时,VM 即达到安全点。因此,检查安全点请求的代码片段称为安全点轮询。
实现需要满足有趣的权衡:安全点轮询几乎从不触发,因此当未触发时它们应该非常高效。
实验
源码
import org.openjdk.jmh.annotations.*;
import java.util.concurrent.TimeUnit;
@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.