`
lvwenwen
  • 浏览: 953947 次
  • 性别: Icon_minigender_1
  • 来自: 魔都
社区版块
存档分类
最新评论

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

    博客分类:
  • Jvm
阅读更多

面先说一下环境,比如现在有两个类,A和B,两个类都是单例类,这个时候如果A有个B的实例变量,B有个A的实例变量,会发生什么情况呢?开始我以为会出现栈溢出。但是让我迷惑的是,居然没问题。只是其中一个类的 实例变量会是NULL。下面看代码。
Java代码 
public class A {  
    private static A a = new A();  
 
    private B b = B.getInstance();  
 
    private A() {  
 
    }  
 
    public static A getInstance() {  
        System.out.println("A被调用");  
        return a;  
    }  
 
    public void test() {  
        System.out.println(b);  
    }  


public class A {
private static A a = new A();

private B b = B.getInstance();

private A() {

}

public static A getInstance() {
System.out.println("A被调用");
return a;
}

public void test() {
System.out.println(b);
}
}



Java代码 
public class B {  
    private static B b = new B();  
 
    private A a = A.getInstance();  
 
    private B() {  
 
    }  
 
    public static B getInstance() {  
        System.out.println("B被调用");  
        return b;  
    }  
      
    public void test() {  
        System.out.println(a);  
    }  


public class B {
private static B b = new B();

private A a = A.getInstance();

private B() {

}

public static B getInstance() {
System.out.println("B被调用");
return b;
}

public void test() {
System.out.println(a);
}
}

Java代码 
  public class Test {  
 
/** 
* @param args 
*/ 
public static void main(String[] args) {  
                
    B b = B.getInstance();  
    b.test();  
    System.out.println("==========");             
    A a = A.getInstance();  
    a.test();  
 
                      
    //A a = A.getInstance();  
    //a.test();   
                    //System.out.println("==========");               
                   //B b = B.getInstance();  
    //b.test();  
                 //System.out.println("==========");      
 
}  

   public class Test {

/**
* @param args
*/
public static void main(String[] args) {
              
B b = B.getInstance();
b.test();
System.out.println("==========");
A a = A.getInstance();
a.test();

               
//A a = A.getInstance();
//a.test();
                 //System.out.println("==========");
                    //B b = B.getInstance();
//b.test();
                  //System.out.println("==========");

}

}


得到的结果是:
B被调用
A被调用
B被调用
A@35ce36
==========
A被调用
null


我原本在第一个getInstance的时候会出现 递归调用初始化 而
死锁的问题。但是没有,你知道JVM对于 构造方法的初始化是如何实现的。大家说说。

引用 收藏

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

类Test的main()方法的B.getInstance()生成的invokestatic指令触发了类B的初始化
执行类B的<clinit>方法的过程中,显式调用了B自己的<init>方法(static b = new B())
B的<init>方法中,调用A.getInstance()生成的invokestatic指令触发了A的初始化(private A a = A.getInstance())
执行A的<clinit>方法的过程中调用了A自己的<init>方法(static a = new A())
类A的<init>方法中需要调用B.getInstance(),但是虚拟机中一个类(<class,classloader>为一个类)只会初始化一次,因此不会再触发B的初始化,既不会再执行B.<clinit>方法。
那由a.<init>触发的B.getInstance()被执行,输出第一行“B被调用”,B.getInstance()方法结束。虽然这时候B的初始化阶段尚未结束,但是解析阶段已经完成,所以getInstance()方法可以被正确执行,但这时候静态字段static B b仍然为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的值。


这样写应该比较好懂了吧?
引用 收藏

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

B-->   B.getInstance()  --- new  B()
                    |                                      |
                    |                                      |
                    |                                      |
                new  A()  < ----    A.getInstance()

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

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

类Test的main()方法的B.getInstance()生成的invokestatic指令触发了类B的初始化
执行类B的<clinit>方法的过程中,显式调用了B自己的<init>方法(static b = new B())
B的<init>方法中,调用A.getInstance()生成的invokestatic指令触发了A的初始化(private A a = A.getInstance())
执行A的<clinit>方法的过程中调用了A自己的<init>方法(static a = new A())
类A的<init>方法中需要调用B.getInstance(),但是虚拟机中一个类(<class,classloader>为一个类)只会初始化一次,因此不会再触发B的初始化,既不会再执行B.<clinit>方法。
那由a.<init>触发的B.getInstance()被执行,输出第一行“B被调用”,B.getInstance()方法结束。虽然这时候B的初始化阶段尚未结束,但是解析阶段已经完成,所以getInstance()方法可以被正确执行,但这时候静态字段static B b仍然为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>为一个类)只会初始化一次。
分享到:
评论

相关推荐

    Java语言规范和JVM虚拟机规范.zip

    1. **类加载子系统**:描述了如何加载、验证、准备、解析和初始化类。 2. **运行时数据区**:包括堆、方法区、栈、本地方法栈和程序计数器,它们分别存储不同类型的运行时数据。 3. **字节码执行引擎**:解释执行...

    JVM虚拟机复习宝典

    3. **对象构造**:在分配好内存之后,JVM将执行构造方法,以初始化对象的状态。构造过程包括对对象头和实例数据的初始化。 4. **设置对象头**:对象创建完成后,JVM需要为对象设置必要的标志位,如哈希码、GC年龄等...

    深入浅出jvm虚拟机视频大全(jvm性能调优+内存模型+虚拟机原理)

    初始化阶段负责执行类构造器`&lt;clinit&gt;`方法。 #### 2. 字节码执行引擎 JVM的核心组件之一就是字节码解释器,它负责执行字节码指令。此外,JVM还包含一个即时编译器(JIT Compiler),能够将热点代码编译成本地机器...

    jvm虚拟机源码学习资料

    加载是找到类的二进制数据,验证确保数据的正确性,准备分配静态变量内存,解析将符号引用转为直接引用,初始化执行类构造器。 3. 字节码执行 JVM通过解释器和即时编译器(如HotSpot的C1和C2编译器)共同作用来...

    深入java虚拟机加载初始化

    在深入了解Java虚拟机(JVM)的加载初始化之前,我们先明确一下`Classloader`的角色。简单地说,`Classloader`的主要职责是将编译后的`.class`文件装载到机器的内存中,为后续程序的执行提供必要的条件。这一过程...

    JVM调优篇.pdf

    初始化阶段是执行类构造器方法的过程。方法是由编译器自动收集类中的类变量的赋值操作和静态语句块中的语句合并而成的。虚拟机会保证子方法执行之前,父类的方法已经执行完毕,如果一个类中没有对静态变量赋值也没有...

    JVM虚拟机思维导图,从整体的结构、概念、本质、算法、优化等角度进行梳理

    5. **初始化**:为类的静态变量赋予正确的初始值,执行类构造器方法(`clinit`)的过程。 #### 四、类加载器分类 - **启动类加载器(Bootstrap ClassLoader)**:用于加载核心类库,如`rt.jar`。 - **其他类加载器**...

    java虚拟机规范

    - **初始化**:执行类构造器`()`方法。 #### 运行时数据区 运行时数据区由以下几个部分组成: - **程序计数器**:记录当前线程所执行的字节码指令地址。 - **Java虚拟机栈**:用于存储局部变量表、操作数栈、动态...

    JVM(Java 虚拟机)的详细讲解

    3. **初始化**:执行类构造器`&lt;clinit&gt;`方法,对类进行初始化处理。 #### 内存管理与垃圾回收 内存管理是JVM的重要功能之一,主要涉及对象的创建、存储和销毁。JVM采用分代收集策略,将堆内存分为新生代和老年代。...

    Java静态初始化块和对象初始化块

    静态初始化块是以`static`关键字标识的代码块,它在类被加载到Java虚拟机(JVM)时执行,且只执行一次。静态初始化块常用于设置类级别的变量,或者执行只应执行一次的初始化操作。例如,如果一个类需要在程序运行前...

    30+个视频+深入理解Java虚拟机(jvm优化+内存模型+虚拟机原理)

    其中,类的初始化阶段是执行类构造器`()`的地方,负责对类变量进行初始值设置。 #### 字节码解释与JIT编译 Java程序最初以字节码形式运行,由解释器解释执行。为了提高性能,JIT(Just-In-Time)编译器会在运行时将...

    解析Java虚拟机中类的初始化及加载器的父委托机制共14页

    Java虚拟机(JVM)是Java程序运行的核心,它的内部机制包括了类的加载、链接、初始化等关键过程。在Java编程中,了解这些过程对于优化程序性能、理解和解决类加载问题至关重要。本文将深入探讨Java虚拟机中的类初始...

    java程序初始化顺序

    - 构造器主体:最后,执行当前类的构造器主体,完成对象的具体初始化。 4. **多线程下的初始化**: - 当多个线程同时尝试初始化同一个类时,Java保证只会执行一次类的初始化过程。这是由JVM的同步机制保证的,...

    JVM分享java虚拟机

    其中,验证确保字节码的安全性,准备阶段分配静态变量内存,解析处理符号引用,初始化则执行类构造器。 堆内存是JVM中最大的一块内存区域,用于存储对象实例。新生代和老年代是堆内存的两个主要部分,新生代主要...

    探究java的ClassLoader及类变量初始化顺序

    需要注意的是,静态初始化块只在类被加载时执行一次,而实例初始化块(构造函数)则会在每次创建类的实例时执行。静态变量的初始化是在类加载时完成的,而实例变量的初始化则在对象创建时进行。 在实际开发中,对...

    类初始化和实例初始化1

    当Java虚拟机(JVM)首次遇到某个类的实例或者静态变量,或者当类的静态方法被调用时,会触发类的加载和初始化。类的初始化主要涉及到以下几个步骤: 1. 类加载:JVM会通过类加载器将类的.class文件加载到内存中。 ...

    java代码初始化流程研究

    这两个初始化块的内容会被编译器插入到对应的构造函数和`&lt;clinit&gt;`方法中。 最后,工具在Java代码初始化中的作用不容忽视。例如,IDE如IntelliJ IDEA和Eclipse可以帮助开发者调试和理解初始化流程,通过断点、变量...

Global site tag (gtag.js) - Google Analytics