Java 类加载过程

BUyd...kbz9
1 Jul 2024
24

1、JVM中类的生命周期

JVM中类的生命周期,包括加载、验证、准备、解析、初始化、使用、卸载等七个阶段,
验证、准备、解析统称为链接。

2、JVM中类加载各个阶段的内容

加载:读取二进制字节流文件(由jar包、war包、网络、动态代理、JSP生成等),在内存中生存对应的java.lang.Class对象存入元空间。
验证:检测Class 文件的字节流中包含的信息是否符合《Java 虚拟机规范》的全部约束要求
准备:类变量(而不是实例变量)赋默认初始值;常量直接赋正式的值。
解析:将常量池内的符号引用替换为直接引用。
初始化:这一步 JVM 才开始真正执行类中定义的 Java 程序代码(字节码),
对于初始化阶段,虚拟机严格规范了有且只有 6 种情况下,必须对类进行初始化(只有主动去使用类才会初始化类):

  1. 当遇到 newgetstaticputstaticinvokestatic 这 4 条字节码指令时,比如 new 一个类,读取一个静态字段(未被 final 修饰)、或调用一个类的静态方法时。
    1. 当 jvm 执行 new 指令时会初始化类。即当程序创建一个类的实例对象。
    2. 当 jvm 执行 getstatic 指令时会初始化类。即程序访问类的静态变量(不是静态常量,常量会被加载到运行时常量池)。
    3. 当 jvm 执行 putstatic 指令时会初始化类。即程序给类的静态变量赋值。
    4. 当 jvm 执行 invokestatic 指令时会初始化类。即程序调用类的静态方法。
  2. 使用 java.lang.reflect 包的方法对类进行反射调用时如 Class.forName("...")newInstance() 等等。如果类没初始化,需要触发其初始化。
  3. 初始化一个类,如果其父类还未初始化,则先触发该父类的初始化。
  4. 当虚拟机启动时,用户需要定义一个要执行的主类 (包含 main 方法的那个类),虚拟机会先初始化这个类。
  5. MethodHandleVarHandle 可以看作是轻量级的反射调用机制,而要想使用这 2 个调用,就必须先使用 findStaticVarHandle 来初始化要调用的类。
  6. 「补充,来自issue745open in new window」 当一个接口中定义了 JDK8 新加入的默认方法(被 default 关键字修饰的接口方法)时,如果有这个接口的实现类发生了初始化,那该接口要在其之前被初始化。

使用:使用这个类
卸载:卸载这个类,需要满足以下条件:

  1. 该类的所有的实例对象都已被 GC,也就是说堆不存在该类的实例对象。
  2. 该类没有在其他任何地方被引用
  3. 该类的类加载器的实例已被 GC(JDK 自带的 BootstrapClassLoader, ExtClassLoaderAppClassLoader 负责加载 JDK 提供的类,所以它们(类加载器的实例)肯定不会被回收。而我们自定义的类加载器的实例是可以被回收的,所以使用我们自定义加载器加载的类是可以被卸载掉的。

3、执行顺序

当new一个对象的时候执行顺序如下:
静态常量赋值(准备阶段)-->静态变量赋默认值(准备阶段)-->静态变量赋真正的值=静态初始化代码块(初始化阶段,这两个同级,谁在前面执行谁)-->实例变量赋值=实例代码块执行(new 创建对象,这两个同级,谁在前面执行谁)-->构造器(new 创建对象)

4、继承时的执行顺序


public class Test2 {
        public static void main(String[] args) {
            Child child = new Child();
        }

}

class Parent {
    static int parentStaticVar = 1;//1
    static {
        System.out.println("2、Parent static block");//2
    }
    int parentInstanceVar = 2;//5
    {
        System.out.println("6、Parent instance block");//6
    }
    public Parent() {
        System.out.println("7、Parent constructor");//7
    }
}

class Child extends Parent {
    static int childStaticVar = 3;//3
    static {
        System.out.println("4、Child static block");//4
    }
    int childInstanceVar = 4;//8
    {
        System.out.println("9、Child instance block");//9
    }
    public Child() {
        System.out.println("10、Child constructor");//10
    }
}

运行结果

2、Parent static block
4、Child static block
6、Parent instance block
7、Parent constructor
9、Child instance block
10、Child constructor


Write & Read to Earn with BULB

Learn More

Enjoy this blog? Subscribe to hideonbush

0 Comments

B
No comments yet.
Most relevant comments are displayed, so some may have been filtered out.