虚拟机把描述类的信息从class文件加载到内存,并对数据进行校验,转化解析和初始化,最终形成可以被虚拟机使用的java类型,这就是虚拟机的类加载机制。
在Java语言里面,类型的加载,链接和初始化过程都是在程序运行期间完成的,Java可以动态扩展就是依赖这个特性实现的。
类加载时机
类从被加载到内存中开始,直到被卸载,它的整个生命周期包括:加载(Loading),验证(Verifaction),准备(Preparation),解析(Resolution),初始化(Initialization),使用(Using),卸载(Unloading)七个过程,其中验证,准备,解析三个阶段统称为连接(Linking)。加载,验证,准备,初始化,卸载这5个阶段的顺序是确定的,类的加载过程必须按照这种顺序按部就班的开始,这些阶段通常是相互交叉地混合式进行,通常会在一个阶段的过程中调用,激活另一个阶段。
Java虚拟机规范只对“初始化”过程的触发时机有严格的定义,有且只有以下5中情况,必须立即对类进行初始化:
- 遇到new、getstatic、putstatic或invokestatic这4条字节码指令时,如果类没有进行过初始化,则需要先触发其初始化。
- 使用java.lang.reflect包的方法对类进行反射调用的时候,如果类没有进行过初始化,则需要先触发其初始化。
- 当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化。
- 当虚拟机启动时,用户需要指定一个要执行的主类(包含main()方法的那个类),虚拟机会先初始化这个主类。
- 当使用JDK 1.7的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,则需要先触发其初始化。
类加载过程
加载:虚拟机需要完成3件事情
- 通过一个类的全限定名获取定义此类的二进制字节流
- 将这个字节流代表的静态存储结构转化为方法区的运行时数据结构
- 在内存中生成一个代表这个类的java.lang.Class对象,作为这个类在方法区中的各种数据的访问入口
验证:验证时连接阶段的第一步,目的是保证Class文件的字节流中所包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。大致分为4个阶段
- 文件格式验证,验证字节流是否符合Class文件格式规范,并且能被当前虚拟机处理。
- 元数据验证,对字节码信息进行语义解析,验证信息是否满足Java语言规范。
- 字节码验证,通过数据流和控制流分析,确定程序语义时合法的,符合逻辑的。
- 符号引用验证,对类自身以外的信息(常量池中的各种符号引用)进行匹配性校验。
准备:在方法区中为类变量分配内存并设置类变量的初始值(数据类型的零值)
解析:将常量池中的符号引用替换为直接引用,啥是符号引用,啥是直接引用?
- 符号引用是指以一组符号来描述引用的目标,符号可以是任何形式的字面量,只要在使用时能无歧义的定位到目标即可
- 直接引用是指直接执行目标的指针,相对偏移量,或者一个能够间接定位到目标的句柄.
初始化:开始执行类中定义的Java程序,根据程序的主观计划初始化类变量和其他资源,是执行类构造器<init>()方法的过程
类加载器
通过一个类的全限定名获取描述此类的二进制字节流 这个动作放到Java虚拟机外部实现,一边让程序直接决定如果获取所需要的类,实现这个动作的代码模块就叫 “类加载器”
每一个类加载器都有一个类命名空间,对于任何一个类,都需要有加载它的类加载器和它本身一同来确定它在虚拟机中的唯一性。
双亲委派模型:
代码热部署: