JVM - 类加载机制

2019/09/08

2020-03-29更新

参考自两道面试题,带你解析Java类加载机制

类加载机制三大步骤

1.加载 - Loading

  • 将什么东西加载到哪里?

将.class文件加载到JVM内存中,并在方法区创建对应的Class对象。

  • Class对象的结构是怎么样?

包含:魔数、主次版本号、常量池、父类信息及接口、字段、方法、属性等。

  • 如何获得.class文件?

编译.java文件生成.class文件

2.连接 - Linking

  • 校验 - Verification
    • 主要分为四个阶段:
      • 第一,Class文件格式校验,例如: 检查魔数、主次版本号。
      • 第二,元数据校验,主要查看是否有父类等。
      • 第三,字节码校验,例如: 将一个子类对象赋值给父类数据类型,JVM认为这是安全的。
      • 第四,符号引用校验,检查是否能通过全限定名找到对应Class类。
    • 作用? 确保.Class文件的字节流数据符合当前虚拟机的要求,并保证虚拟机的安全。
  • 准备 - Preparation
    • 作用? - 为类成员变量方法区分配内存,并设置零值
public static int static_variable = 1;
public static final int static_final_variable = 2;

注意,准备阶段过后,static_variable的值为0,而static_final_variable的值为2

  • 解析 - Resoultion
    • 作用? 虚拟机将方法区中常量池的符号引用替换为直接引用

3.初始化 - Initialization

  • 什么时候触发?

主要分为以下四种:

  • 使用newgetstaticputstaticinvokestatic指令。
  • 父类优先原则:当初始化一个类时,若其父类尚未初始化,则先初始化其父类。
  • 进行反射调用时。
  • 主类优先原则:包含main方法的类优先初始化。

举个例子:

public class Parent {

    static int staticVar = 10;
    int instanceVar = 20;

    public Parent(){
        System.out.println("Execute constructor in Parent class.");
    }

    static {
        System.out.println("Execute static block in Parent class.");
    }

    {
        System.out.println("Execute common block in Parent class.");
    }

    static void staticMethod(){
        System.out.println("Execute staticMethod() in Parent class.");
    }

    public void instanceMethod(){
        System.out.println("Execute instanceMethod() in Parent class.");
    }

    public static void main(String[] args) {
        System.out.println("Execute main() in Parent class.");
    }
}

public class Child {

    public static void main(String[] args) throws NoSuchMethodException,
            IllegalAccessException, InvocationTargetException,
            InstantiationException {
        /** 1.1. new **/
        new Parent();

//        Execute static block in Parent class.
//        Execute common block in Parent class.
//        Execute constructor in Parent class.

        /** 1.2. getStatic **/
        int ret = Parent.staticVar;
//        Execute static block in Parent class.

        /** 1.3. putStatic **/
        Parent.staticVar = 2;
//        Execute static block in Parent class.

        /** 1.4. invokeStatic **/
        Parent.staticMethod();
//        Execute static block in Parent class.
//        Execute staticMethod() in Parent class.

        /** 2. Parent first **/
        new Child();
//        Execute static block in Parent class.
//        Execute common block in Parent class.
//        Execute constructor in Parent class.

        /** 3. Reflection **/
        Class<Parent> parentClass = Parent.class;
        Parent parent = parentClass.getConstructor().newInstance();
        parent.instanceMethod();

//        Execute static block in Parent class.
//        Execute common block in Parent class.
//        Execute constructor in Parent class.
//        Execute instanceMethod() in Parent class.
    }
}
  • 初始化过程?

分为两步:

  • 类初始化:静态代码块、静态赋值语句。
  • 实例初始化:普通代码块、普通赋值语句和构造器。

举个例子:

public class Initialization {

    int instanceVar = 5;
    static int staticVar = 10;

    {
        System.out.println("Execute common block: instanceVar = " + instanceVar);
    }

    static {
        staticVar = 20;
        System.out.println("Execute static block: staticVar = " + staticVar);
    }

    public Initialization() {
        System.out.println("Execute constructor in Initialization.");
    }

    public static void main(String[] args) {
        System.out.println("Execute main() in Initialization.");
        new Initialization();
    }
}

// Execute static block: staticVar = 20
// Execute main() in Initialization.
// Execute common block: instanceVar = 5
// Execute constructor in Initialization.


类加载器分类

  • 引导类加载器(Bootstrap ClassLoader)
  • 扩展类加载器(Extension ClassLoader)
  • 系统类加载器(System ClassLoader)
  • 用户类加载器(User ClassLoader)


疑问

1.Java虚拟机规范允许系统预加载某些类。

  • 哪些类? 系统经常用到的,如:java.util.*java.lang.*sun.reflect.*
  • 如何证明? 启动项目时添加-XX:+TraceClassLoading虚拟机参数。

pre-load-class


参考

  • 本文中,有些细节没有展开论述,原因是发现一篇很好的文章 Java虚拟机9:Java类加载机制 里面已经讲得很清楚了,我也就没有必要再做赘述。

  • 《深入理解Java虚拟机》 - 第七章(虚拟机类加载机制)
  • 《疯狂Java讲义》 - 第十八章(类加载机制与反射)


一位喜欢提问、尝试的程序员

(转载本站文章请注明作者和出处 姚屹晨-yaoyichen

Post Directory