Java 类加载过程
27
1、JVM中类的生命周期
JVM中类的生命周期,包括加载、验证、准备、解析、初始化、使用、卸载等七个阶段,
验证、准备、解析统称为链接。
2、JVM中类加载各个阶段的内容
加载:读取二进制字节流文件(由jar包、war包、网络、动态代理、JSP生成等),在内存中生存对应的java.lang.Class对象存入元空间。
验证:检测Class 文件的字节流中包含的信息是否符合《Java 虚拟机规范》的全部约束要求
准备:类变量(而不是实例变量)赋默认初始值;常量直接赋正式的值。
解析:将常量池内的符号引用替换为直接引用。
初始化:这一步 JVM 才开始真正执行类中定义的 Java 程序代码(字节码),
对于初始化阶段,虚拟机严格规范了有且只有 6 种情况下,必须对类进行初始化(只有主动去使用类才会初始化类):
- 当遇到
new
、getstatic
、putstatic
或invokestatic
这 4 条字节码指令时,比如new
一个类,读取一个静态字段(未被 final 修饰)、或调用一个类的静态方法时。- 当 jvm 执行
new
指令时会初始化类。即当程序创建一个类的实例对象。 - 当 jvm 执行
getstatic
指令时会初始化类。即程序访问类的静态变量(不是静态常量,常量会被加载到运行时常量池)。 - 当 jvm 执行
putstatic
指令时会初始化类。即程序给类的静态变量赋值。 - 当 jvm 执行
invokestatic
指令时会初始化类。即程序调用类的静态方法。
- 当 jvm 执行
- 使用
java.lang.reflect
包的方法对类进行反射调用时如Class.forName("...")
,newInstance()
等等。如果类没初始化,需要触发其初始化。 - 初始化一个类,如果其父类还未初始化,则先触发该父类的初始化。
- 当虚拟机启动时,用户需要定义一个要执行的主类 (包含
main
方法的那个类),虚拟机会先初始化这个类。 MethodHandle
和VarHandle
可以看作是轻量级的反射调用机制,而要想使用这 2 个调用,就必须先使用findStaticVarHandle
来初始化要调用的类。- 「补充,来自issue745open in new window」 当一个接口中定义了 JDK8 新加入的默认方法(被 default 关键字修饰的接口方法)时,如果有这个接口的实现类发生了初始化,那该接口要在其之前被初始化。
使用:使用这个类
卸载:卸载这个类,需要满足以下条件:
- 该类的所有的实例对象都已被 GC,也就是说堆不存在该类的实例对象。
- 该类没有在其他任何地方被引用
- 该类的类加载器的实例已被 GC(JDK 自带的
BootstrapClassLoader,
ExtClassLoader
,AppClassLoader
负责加载 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