JVM虚拟机如何来初始化构造方法的

面先说一下环境,比如现在有两个类,A和B,两个类都是单例类,这个时候如果A有个B的实例变量,B有个A的实例变量,会发生什么情况呢?开始我以为会出现栈溢出。但是让我迷惑的是,居然没问题。只是其中一个类的实例变量会是NULL。下面看代码。

Java代码

publicclassA{

privatestaticAa=newA();

privateBb=B.getInstance();

privateA(){

}

publicstaticAgetInstance(){

System.out.println("A被调用");

returna;

}

publicvoidtest(){

System.out.println(b);

}

}

publicclassA{

privatestaticAa=newA();

privateBb=B.getInstance();

privateA(){

}

publicstaticAgetInstance(){

System.out.println("A被调用");

returna;

}

publicvoidtest(){

System.out.println(b);

}

}

Java代码

publicclassB{

privatestaticBb=newB();

privateAa=A.getInstance();

privateB(){

}

publicstaticBgetInstance(){

System.out.println("B被调用");

returnb;

}

publicvoidtest(){

System.out.println(a);

}

}

publicclassB{

privatestaticBb=newB();

privateAa=A.getInstance();

privateB(){

}

publicstaticBgetInstance(){

System.out.println("B被调用");

returnb;

}

publicvoidtest(){

System.out.println(a);

}

}

Java代码

publicclassTest{

/**

*@paramargs

*/

publicstaticvoidmain(String[]args){

Bb=B.getInstance();

b.test();

System.out.println("==========");

Aa=A.getInstance();

a.test();

//Aa=A.getInstance();

//a.test();

//System.out.println("==========");

//Bb=B.getInstance();

//b.test();

//System.out.println("==========");

}

publicclassTest{

/**

*@paramargs

*/

publicstaticvoidmain(String[]args){

Bb=B.getInstance();

b.test();

System.out.println("==========");

Aa=A.getInstance();

a.test();

//Aa=A.getInstance();

//a.test();

//System.out.println("==========");

//Bb=B.getInstance();

//b.test();

//System.out.println("==========");

}

}

得到的结果是:

B被调用

A被调用

B被调用

A@35ce36

==========

A被调用

null

我原本在第一个getInstance的时候会出现递归调用初始化而

死锁的问题。但是没有,你知道JVM对于构造方法的初始化是如何实现的。大家说说。

引用收藏

IcyFenix2011-04-09

大致的执行过程如下,本来想直接贴Javap的字节码了事的,不过整理了一下后发现贴了估计也看不清楚,所以过程还是用中文大致写了一些:

类Test的main()方法的B.getInstance()生成的invokestatic指令触发了类B的初始化

执行类B的<clinit>方法的过程中,显式调用了B自己的<init>方法(staticb=newB())

B的<init>方法中,调用A.getInstance()生成的invokestatic指令触发了A的初始化(privateAa=A.getInstance())

执行A的<clinit>方法的过程中调用了A自己的<init>方法(statica=newA())

类A的<init>方法中需要调用B.getInstance(),但是虚拟机中一个类(<class,classloader>为一个类)只会初始化一次,因此不会再触发B的初始化,既不会再执行B.<clinit>方法。

那由a.<init>触发的B.getInstance()被执行,输出第一行“B被调用”,B.getInstance()方法结束。虽然这时候B的初始化阶段尚未结束,但是解析阶段已经完成,所以getInstance()方法可以被正确执行,但这时候静态字段staticBb仍然为null(注意,b.<init>方法还没完呢),所以这个B.getInstance()方法返回值为null,所以A.b为null。

A.<clinit>方法结束,第3步的invokestatic指令正式执行,即A.getInstance()被执行,输出第二行“A被调用”,返回A的实例,这时候B.a不为null了。

B.<clinit>方法结束,第1步的invokestatic指令正式执行,即B.getInstance()被执行,输出第三行“B被调用”

B.test()方法被执行,输出B.a的toString()方法结果,即第四行“A@35ce36”。

A.getInstance()方法被执行,输出第五行“A被调用”。

A.test()方法被执行,输出null,即A.b的值。

这样写应该比较好懂了吧?

引用收藏

Anddy2011-04-09

如果你的想法跟我一样,看看我的分析流程,就一下子明白。

B-->B.getInstance()---newB()

||

||

||

newA()<----A.getInstance()

在newA()中调用B.getInstance(),returnb;但是b是静态成员,在B类加载的时候已经记录在静态存储区【只是值为null】。不会在去调用B.getInstance()方法,所以这个死锁不存在。

引用收藏

xgj19882011-04-10

IcyFenix写道

大致的执行过程如下,本来想直接贴Javap的字节码了事的,不过整理了一下后发现贴了估计也看不清楚,所以过程还是用中文大致写了一些:

类Test的main()方法的B.getInstance()生成的invokestatic指令触发了类B的初始化

执行类B的<clinit>方法的过程中,显式调用了B自己的<init>方法(staticb=newB())

B的<init>方法中,调用A.getInstance()生成的invokestatic指令触发了A的初始化(privateAa=A.getInstance())

执行A的<clinit>方法的过程中调用了A自己的<init>方法(statica=newA())

类A的<init>方法中需要调用B.getInstance(),但是虚拟机中一个类(<class,classloader>为一个类)只会初始化一次,因此不会再触发B的初始化,既不会再执行B.<clinit>方法。

那由a.<init>触发的B.getInstance()被执行,输出第一行“B被调用”,B.getInstance()方法结束。虽然这时候B的初始化阶段尚未结束,但是解析阶段已经完成,所以getInstance()方法可以被正确执行,但这时候静态字段staticBb仍然为null(注意,b.<init>方法还没完呢),所以这个B.getInstance()方法返回值为null,所以A.b为null。

A.<clinit>方法结束,第3步的invokestatic指令正式执行,即A.getInstance()被执行,输出第二行“A被调用”,返回A的实例,这时候B.a不为null了。

B.<clinit>方法结束,第1步的invokestatic指令正式执行,即B.getInstance()被执行,输出第三行“B被调用”

B.test()方法被执行,输出B.a的toString()方法结果,即第四行“A@35ce36”。

A.getInstance()方法被执行,输出第五行“A被调用”。

A.test()方法被执行,输出null,即A.b的值。

这样写应该比较好懂了吧?

但是虚拟机中一个类(<class,classloader>为一个类)只会初始化一次。

相关推荐