类加载机制

JAVA虚拟机 把描述类的数据 从 class文件 加载到内存,并对数据进行校验、转换解析和初始化,最后形成可以被虚拟机直接使用的指令和数据 的这个过程 叫做虚拟机的类加载机制。

JAVA类加载的 加载、连接、初始化过程整个发生在 程序运行期间。虽然比起编译型程序多了类加载过程的开销,但是增加了程序的扩展性和灵活性。

 

 

一、一个class文件从硬盘到内存的整个生命周期

加载Loading -> [验证Verification,准备Preparation,解析Resolution] -> 初始化Initialization -> 使用Using -> 卸载Unloading

 

1.1 JVMS规定了6种必须立即对类进行初始化的情况

1. 遇到new、getstatic、putstatic、invokestatic这四条字节码指令时。

触发这些指令的情况有: 使用new 关键字实例化对象时。读取或设置一个类的静态字段(被final修饰、已在编译期把结果放到常量池的静态字段除外)时。调用一个类的静态方法时。

2. 使用java.lang.reflect包的方法对类进行反射调用的时候,如果类没有初始化过需要触发。

3. 当初始化类时,如果它的父类还没有被初始化,那么需要先触发父类的初始化。

4. jvm启动时需要用户指定一个要执行的主类(包含main方法),虚拟机会先初始化这个主类。

5. 如果一个java.lang.invoke.MethodHandle实例最后的解析结果为REF_getStatic,REF_putStatic,REF_invokeStatic,REF_newInvokeSpecial四种类型的方法句柄,并且这个方法句柄对应的类没有进行初始化,则需要先触发。

6. 当一个接口定义了default方法,如果这个接口的实现类想初始化,则接口要在实现类前初始化。

 

有且只有这6种情况会初始化类。

public class Father {
    static {
        System.out.println("in Father static");
    }
    public static int i = 10;
}

public class Son extends Father{
    static {
        System.out.println("in son static");
    }
    public static  int value = 10;
}

public class T {
    public static void main(String[] args) {
        System.out.println(Son.value);
    }
}
//in Father static
//in son static
//10

如果谁Father.i,只会打印 in Father static。 因为只直接调用了父类的静态属性,而实例化Son实,要满足 情况3 先初始化父类。

 

 

二、加载过程

2.1 加载Loading

 

 

2.3 准备

准备阶段 正式 将类中定义的变量(即静态变量,被static修饰的)分配内存并设置初始值的阶段。

概念上将这些变量的内存都在堆中的方法区中。因为每个类都在方法区中存储着字段信息、常量池和方法代码。

public static int age = 123;

准备阶段结束后age是0,因为把age赋值为123的putstatic指令要在初始化阶段执行类构造器的cinit()方法后才执行。

 

2.4 解析

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

笔记来自:

【1】深入理解JAVA虚拟机:JVM高级特性与最佳实践