【发布时间】:2020-10-21 19:23:03
【问题描述】:
正在研究 JVM 规范/内部结构,并想了解循环引用的递归类初始化应该如何正确发生。看这个例子:
class CA extends Object {
public final int ivar = 1;
public static CB other = new CB();
public CA() {
System.out.println("in CA.init, my ivar is " + this.ivar);
}
}
class CB extends Object {
public final int ivar = 2;
public static CA other = new CA();
public CB() {
System.out.println("in CB.init, my ivar is " + this.ivar);
}
public static void main(String[] args) {
CB cb = new CB();
}
}
执行此结果:
in CB.init, my svar is 2
in CA.init, my ivar is 1
in CB.init, my svar is 2
这些反映了实例初始化并且有意义。 class 初始化,但必须像这样运行:
- CB
<clinit>实例化一个 CA,它应该触发... - CA
<clinit>,它实例化一个 CB,它尝试一个 - CB
<clinit>再次进行中...
JVM 规范在 s5.5 初始化下说:
- 如果 C 的 Class 对象指示当前线程正在对 C 进行初始化,则这必须是初始化的递归请求。释放 LC 并正常完成。
这意味着在我上面的第 3 步中,JVM 耸了耸肩,然后返回完成第 2 步。但是完成第 2 步意味着在新的 CB 实例上调用构造函数 <init>。当 CB 类还没有完成它的<clinit> 时,它怎么能做到这一点?
在这种情况下,因为对象没有对它们持有的彼此实例“做任何事情”,所以没有伤害就没有犯规。但是我应该如何考虑这里的行为和潜在的陷阱?谢谢。
【问题讨论】:
标签: java jvm language-lawyer