栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

为什么在静态初始化程序中使用并行流会导致不稳定的死锁

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

为什么在静态初始化程序中使用并行流会导致不稳定的死锁

TL; DR
这是HotSpot错误JDK-8215634

可以通过一个根本没有种族的简单测试用例来重现该问题:

public class StaticInit {    static void staticTarget() {        System.out.println("Called from " + Thread.currentThread().getName());    }    static {        Runnable r = new Runnable() { public void run() {     staticTarget(); }        };        r.run();        Thread thread2 = new Thread(r, "Thread-2");        thread2.start();        try { thread2.join(); } catch (Exception ignore) {}        System.out.println("Initialization complete");    }    public static void main(String[] args) {    }}

这看起来像经典的初始化死锁,但是HotSpot JVM不会挂起。而是打印:

Called from mainCalled from Thread-2Initialization complete

为什么这是一个错误

JVMS§6.5要求在执行

invokestatic
字节码时

如果尚未初始化声明已解析方法的类或接口,则该类或接口尚未初始化

Thread-2
通话
staticTarget
,主类
StaticInit
显然是未初始化(因为它的静态初始化仍在运行)。这意味着
Thread-2
必须启动JVMS§5.5中描述的类初始化过程。按照这个程序

  1. 如果C的Class对象指示其他线程正在对C进行初始化,则释放LC并阻塞当前线程,直到得知正在进行的初始化已完成

但是,

Thread-2
尽管该类正在通过thread进行初始化,但不会被阻塞
main

那其他JVM呢

我测试了OpenJ9和JET,预期它们在上述测试中都陷入僵局。
有趣的是,HotSpot也可以挂在

-Xcomp
模式下,但不能挂在
-Xint
或混合模式下。

怎么发生的

解释器首次遇到

invokestatic
字节码时,它将调用JVM运行时来解析方法引用。作为此过程的一部分,JVM会在必要时初始化该类。成功解决后,解决的方法将保存在“常量池缓存”条目中。常量池缓存是特定于HotSpot的结构,用于存储解析的常量池值。

在上面的测试

invokestatic
字节码中,调用
staticTarget
首先由
main
线程解析。解释器运行时将跳过类的初始化,因为该类已经被同一线程初始化。解决的方法保存在常量池缓存中。下次
Thread-2
执行相同的操作时
invokestatic
,解释器会看到字节码已被解析,并使用常量池高速缓存条目而不调用运行时,从而跳过了类初始化。

类似的bug对

getstatic
/
putstatic
不久前固定-
JDK-4493560,但修复没有触及
invokestatic
。我已提交了新的错误JDK-8215634以解决此问题。

至于原来的例子

是否挂起取决于哪个线程首先解析了静态调用。如果它是

main
线程,则程序将完成而不会出现死锁。如果通过
ForkJoinPool
线程之一解决了静态调用,程序将挂起。

更新资料

该错误已确认。在即将发布的版本中已修复该问题:JDK 8u201,JDK 11.0.2和JDK 12。



转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/507598.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号