JVM类初始化过程

JVM类初始化过程

目录

类加载机制

类加载机制主要有三步: 加载、连接、初始化。

加载

  • ​ 把编译好的class文件加载进内存 (1.7及之前为方法区,1.8取消方法区,用元数据区替代) 中,并在堆中创建对应的Class对象。

连接

验证
  • ​ 验证加载的类信息是否符合JVM规范,确保安全
准备
  • ​ 为静态变量分配内存并设置默认的初始值
解析
  • ​ 将类的二进制数据中的符号引用替换成直接引用

初始化

  • ​ 静态变量赋值 -> 静态代码块 -> 成员变量 -> 构造代码块 -> 构造方法

  • 其中,成员变量、构造代码块和构造方法在实例化时即创建对象时才执行

使用

卸载

  • 以上五个阶段(加载 连接 初始化 使用 卸载)为一个类的生命周期,在此并不做深入讨论

示例

Father.java
public class Father {
    private static String staticMember;
    private static A a = new A();
    static {
        System.out.println("执行Father的static块前 staticMember: " + staticMember);
        System.out.println("执行Father的static块前 a: " + a);
        System.out.println("执行Father的static块");
        staticMember = "静态成员变量";
        System.out.println("执行Father的static块后 staticMember: " + staticMember);
        System.out.println("执行Father的static块后 a: " + a);
    }
    private String member;
    private A a1 = new A();
    public Father() {
        System.out.println("执行Father的无参构造方法前 member: " + member);
        System.out.println("执行Father的无参构造方法前 a1: " + a1);
        System.out.println("执行Father的无参构造方法");
        member = "成员变量";
        System.out.println("执行Father的无参构造方法后 member: " + member);
        System.out.println("执行Father的无参构造方法后 a1: " + a1);
    }
    public Father(String member) {
        this.member = member;
        System.out.println("执行Father的有参构造方法");
    }
}
Son.java
public class Son extends Father {

    private A a = new A();
    private static B b = new B();

    static {
        System.out.println("执行Son的static块 b: " + b);
    }

    public Son(){
        System.out.println("执行Son的无参构造方法 a : " + a);
    }

    public static void main(String[] args) {
        System.out.println(1);
        Son son;
        son = new Son();
        System.out.println("son : " + son);
    }
}
A.java
public class A {

    static {
        System.out.println("执行A的static块");
    }

    public A() {
        System.out.println("执行A的无参构造方法");
    }
}
B.java
public class B {

    static {
        System.out.println("执行B的static块");
    }

    public B() {
        System.out.println("执行B的无参构造方法");
    }
}

文字流程

因为程序入口在Son类 需进行加载、连接、初始化

  1. 因为Son继承Father 所以加载Son之前先加载Father字节码文件 -> 加载Father.class
  2. 加载Father后给静态成员变量分配内存空间并设置默认初始值 -> String staticMember = null; A a = null;
  3. 初始化Father
    1. 为静态成员变量 a 赋值 -> a = new A()
      1. 因为方法区中没有找到A的字节码文件 需要加载A的字节码文件 -> 加载A.class
      2. 初始化A
        1. 执行A的静态代码块
      3. 实例化A
        1. 执行A的无参构造方法
    2. 执行Father的静态代码块
  4. 加载Son字节码文件 ->加载Son.class
  5. 加载Son后给静态成员变量分配内存空间并设置默认初始值 -> B b = null;
  6. 初始化Son
    1. 为静态成员变量 b 赋值 -> b = new B()
      2. 因为方法区中没有找到B的字节码文件 需要加载B的字节码文件 -> 加载B.class
      2. 初始化B
      1. 执行B的静态代码块
      2. 实例化B
        1. 执行B的无参构造方法
    2. 执行Son的静态代码块
  7. 因为Son继承Father 所以实例化Son前先实例化Father -> Son(){ super(); … }
  8. 实例化Father
    1. 为成员变量分配内存空间并设置默认初始值 -> String member = null; A a1 = null;
    2. 为成员变量 a1 赋值 -> a1 = new A()
      1. 因为A.class已经加载进方法区 所以无需再次加载 -> 寻找并得到A.class
      2. 因为A的静态代码块已经随着A的加载执行了 所以不会再次执行 -> 跳过静态代码块
      3. 实例化A
      4. 执行A的无参构造方法
    3. 执行Father的无参构造方法
  9. 实例化Son
    1. 为成员变量分配内存空间并设置默认初始值 -> A a =null;
    2. 为成员变量 a 赋值 -> a = new A()
      1. 因为A.class已经加载进方法区 所以无需再次加载 -> 寻找并得到A.class
      2. 因为A的静态代码块已经随着A的加载执行了 所以不会再次执行 -> 跳过静态代码块
      3. 实例化A
      4. 执行A的无参构造方法
    3. 执行Son的无参构造方法

结果展示

执行结果
JVM类初始化过程

相关内存图

JVM类初始化过程

ps:此图中方法入栈出栈未标出,若标出具体的连线会显得图太乱,便以此图贴出。若文章中有出错的地方,请多多指教,谢谢

上一篇:Java 中的强制类型转换


下一篇:Java 链表节点值