JVM类加载过程
什么是类加载?
在代码编译后,就会生成JVM(Java虚拟机)能够识别的二进制字节流文件(*.class)。而JVM把Class文件中的类描述数据从文件加载到内存,并对数据进行校验、转换解析、初始化,使这些数据最终成为可以被JVM直接使用的Java类型,这个说来简单但实际复杂的过程叫做JVM的类加载机制。
类加载过程分别是什么?
-
Class类文件加载
首先类加载器通过一个类的全限定名获取此类的二进制字节流,将字节流中的静态存储结构转化为运行时方法区的数据结构,然后在内存中生成该类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口,就完成了Class类文件的加载
-
验证
首先会检查二进制字节流合法性
然后校验类型:
- 文件格式验证
- 元数据验证
- 字节码验证:检查局部变量表中slot是否越界使用
- 符号引用验证:符号引用校验发生在解析阶段,符号引用可以看做是类自身以外的的信息进行匹配性校验,其中包含了验证符号引用中的全限定名能否找到对应的类、验证指定类中是否有存在符号方法的字段描述符,以及简单名称所描述的方法和字段、验证符号引用中的类、字段、方法的访问性,能否被当前类访问
-
准备
首先为对象分配内存,然后设置类变量初始值,如int的0,boolean的false,若字段表中存在的ConstantValue,则对应的变量会提前初始化为设定值(若为final变量,提前读取常量池获取值)
-
解析
将符号引用转化为直接引用
-
初始化
触发类初始化的场景:
- 遇到new、getstatic、setstatic、invokestatic,这四条字节码指令,如果类没有初始化,则要先触发初始化。使用new关键字、读取或设置一个类的静态字段(被final修饰、已在编译期就把结果放入常量池的静态字段除外),以及调用静态方法
- 使用java.lang.reflect包的方法对类进行反射调用的时候
- 初始化一个类的时候,如果父类还没有初始化,则需要先触发其父类的初始化
- 当虚拟机启动时,用户指定的主类需要先初始化
- 使用JDK1.7的动态语言支持,如果一个java.lang.invoke.MethodHandle实例最后的解析结果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且这个方法句柄的对应的类没有初始化,需要触发其初始化
什么情况下不会触发初始化?
- 调用父类的static域的变量、方法,不会触发子类的初始化
- 创建一个类的数组,不会触发初始化
- 编译阶段的常量传播优化:在常量在编译阶段会存入调用类的常量池中
-
使用
我们在代码中声明创建之后,进行使用
-
卸载
卸载分为两种情况:
- jvm自带的类加载器:由Java虚拟机自带的类加载器所加载的类,在虚拟机的生命周期中,始终不会被卸载。前面已经介绍过,Java虚拟机自带的类加载器包括根类加载器、扩展类加载器和系统类加载器。Java虚拟机本身会始终引用这些类加载器,而这些类加载器则会始终引用它们所加载的类的Class对象,因此这些Class对象始终是可触及的。
- 用户自定义的类加载器:由用户自定义的类加载器所加载的类是可以被卸载的