TL;博士
这是一个热点错误
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 main
Called from Thread-2
Initialization complete
JVMS §6.5
invokestatic
字节码
什么时候
Thread-2
电话
staticTarget
,主课
StaticInit
显然未初始化(因为其静态初始值设定项仍在运行)。这意味着
螺纹-2
必须启动中所述的类初始化过程
JVMS §5.5
. 按照这个程序,,
-
如果C的类对象指示其他线程正在进行C的初始化,则释放LC并阻止当前线程,直到通知正在进行的初始化已完成
螺纹-2
未被阻止,尽管类正在通过线程进行初始化
main
.
其他JVM呢
我测试了OpenJ9和JET,他们都期望在上述测试中死锁。
有趣的是,热点也存在
-Xcomp
模式,但不在
-Xint
它是怎么发生的
字节码,它调用JVM运行时来解析方法引用。作为该过程的一部分,JVM会在必要时初始化该类。成功解析后,解析的方法保存在常量池缓存项中。常量池缓存是一种特定于热点的结构,用于存储解析的常量池值。
在上述测试中
不动产
调用
静态目标
首先由
主要的
线解释器运行时跳过类初始化,因为该类已由同一线程初始化。解析的方法保存在常量池缓存中。下次什么时候
螺纹-2
执行相同的命令
,解释器看到字节码已经解析,并使用常量池缓存条目而不调用运行时,因此跳过类初始化。
getstatic
/
putstatic
很久以前就修好了-
JDK-4493560
不动产
JDK-8215634
至于原来的例子,,
它是否挂起取决于哪个线程首先解析静态调用。如果是
主要的
线程,程序在没有死锁的情况下完成。如果静态调用由以下其中一个解决:
ForkJoinPool
线程,程序挂起。
错误是
confirmed