GC如何挂起/阻塞应用程序线程

3
我知道当新对象分配失败或调用System.gc()时,垃圾回收机制会被触发。每种垃圾回收算法都建议在第一步中,垃圾回收线程将暂停所有应用程序线程,以便它们不会影响垃圾回收活动。
但是我想了解垃圾回收如何暂停所有正在运行的线程?我的意思是JVM是否定义了安全点,例如内存分配(新对象创建)或方法调用,当应用程序线程到达这些安全点时,它们将被阻止针对GC锁定。这是真的吗?如果是这样,那么以下仅执行简单计算的应用程序线程(我知道在现实中这是不可能的),它会被暂停吗?
while(true) {
    a = a + s;
    s = s + a;

    // some computation that doesn't touch any JVM safe points 
}

在这些情况下,GC活动是否会在不挂起这些应用程序线程的情况下继续进行(并在它们尝试穿过安全点时暂停/阻塞,例如对象分配)?
但我相信,GC总是等待这些应用程序线程进入安全点并在继续之前将它们挂起。我的假设是正确的吗?

只是为了明确起见,System.gc()并不能保证垃圾回收。它取决于多个JVM特定参数。 - Aniket Thakur
没问题,我更关心的是垃圾回收如何暂停正在运行的应用程序线程。 - Sathish
不是所有的垃圾收集器都是STW的。这取决于具体的实现,因此我认为您在VM规范中找不到相关内容。 - Dioxin
是的,这取决于所使用的垃圾回收算法。你可以阅读我之前的一个答案 - Aniket Thakur
好的,假设我的问题与STW GC有关。请问您知道在这种情况下应用程序线程是如何被暂停的吗? - Sathish
有几个可切换的GC;其中一些会暂停所有线程,而有些则不会。 - Maurice Perry
1个回答

7

但我想了解垃圾回收是如何暂停所有正在运行的线程的?

Hotspot实现使用安全点轮询。引用:

安全点是如何工作的?

HotSpot JVM中的安全点协议是合作式的。每个应用程序线程都会检查安全点状态,并在需要时将自己停放在安全状态下。对于已编译的代码,JIT会在特定点(通常是调用返回或循环后跳转之后)的代码中插入安全点检查。对于解释代码,JVM有两个字节码分派表,如果需要安全点,则JVM切换表以启用安全点检查。

安全点状态检查本身的实现非常狡猾。正常的内存变量检查需要昂贵的内存屏障。尽管如此,安全点检查被实现为内存读取屏障。然后,在需要安全点时,JVM取消映射具有该地址的页面,从而在应用程序线程上引发页面故障(由JVM的处理程序处理)。这样,HotSpot可以保持其JITed代码CPU流水线友好,同时确保正确的内存语义(页面取消映射强制内存屏障到处理核心)。

来自机械同理心邮件列表的更详细的描述


 // some computation that doesn't touch any JVM safe points 
编译器只允许那些可以证明在有限时间内完成的事情。否则,它会插入安全点轮询。

我认为这是一个很好的答案,它符合我的假设。谢谢! - Sathish

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接