类加载的生命周期包括:加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和卸载(Unloading)这七个阶段。其中验证、准备、解析统称为连接(Linking),如下图所示。
其中加载、验证、准备、初始化和卸载这五个阶段的顺序是确定的,解析则不一定,在某些情况下可以再初始化之后进行,这是为了支持Java语言的运行时绑定(动态绑定)。
在加载阶段,虚拟机需要完成以下3件事情:
对于类加载的其它过程,一个非数组类的加载过程是可控性最强的。可以使用系统提供的引导类记载器完成,也可以通过用户自定义的类加载器,开发人员可以通过定义自己的类加载器去控制字节流的获取方式(重写一个类加载器的loadClass()方法)。
对于非数组来说来说,数组类本身不通过类加载器创建,是由Java虚拟机直接创建的。但是数组类里的元素类型(Element Type)是靠类加载器创建的。
验证的目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。
验证阶段大致上会完成下面四个阶段的检验动作:文件格式的验证、元数据的验证、字节码验证、符号引用验证。
文件格式的验证:
该阶段的验证是保证输入的字节流能正确解析并存储于方法区内。
这个阶段的验证是基于二进制字节流进行的,只有通过这个阶段的验证,字节流才会进入内存的方法区中进行存储,所以后面的3个验证阶段全部是基于方法的存储结构进行的,不会再直接操作字节流。
例
元数据验证:
主要是对元数据信息进行语义校验,保证不存在不符合Java语言规范的元数据信息。
例
字节码验证:
通过数据流和控制流分析,确定程序语义是合法的、符合逻辑的。
例
符号引用验证
最后一个阶段的校验发生在虚拟机将符号引用转化为直接引用的时候,这个转化动作将发生在解析阶段中(在下面将会介绍)。
例
准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些变量所使用的内存都在方法区中进行。
注:
所谓解析,就是虚拟机将常量池内的符号引用替换成直接引用的过程。
符号引用:以一组符号来描述所引用的目标,符号可以是任何形式的字面量,符号引用与虚拟机实现的内存布局无关。
直接引用:直接指向目标的指针,相对偏移量或是一个能间接定位到目标的句柄,和虚拟机实现的内存布局有关。
解析主要分为以下四个阶段:
类初始化是类加载器过程的最后一步,到了初始化阶段,才真正开始执行类中定义的Java程序代码,主要对类变量进行初始化。
立即对类进行“初始化”的5种情况:
注:所有引用类的方式都不会触发初始化。
以下几种情况会使虚拟机结束生命周期: 1. 执行了System.exit()方法 2. 程序正常执行结束 3. 程序在执行过程中遇到了异常或错误而异常终止 4. 由于操作系统出现错误而导致Java虚拟机进程终止