类加载的过程
类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括:加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和卸载(Unloading)7个阶段。其中验证、准备、解析3个部分统称为连接(Linking),类的声明周期如下:
虚拟机规范严格规定了有且只有以下5种情况必须立即对类进行初始化:
1)遇到new、getstatic、putstatic或invokestatic这4条字节码指令时,如果类没有进行过初始化,则需要先触发其初始化。生成这4条指令的场景有:使用new关键字实例化对象、读取或设置一个类的静态字段(被final修饰、已在编译期把结果放入常量池的静态字段除外),以及调用一个类的静态方法的时候。
2)使用java.lang.reflect包的方法对类进行反射调用的时候,如果类没有进行过初始化,则需要触发其初始化。
3)当初始化一个类的时候,如果发现其父类还没有初始化,则需要先触发其父类的初始化。
4)当虚拟机启动时,用户需要指定一个要执行的主类(包含main方法的那个类),虚拟机会先初始化这个类
5)当使用JDK1.7的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果是REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,则需要先触发其初始化。
加载
加载是类加载的的一个阶段。加载阶段的主要步骤:
1)通过一个类的全限定名来获取定义此类的二进制字节流(不限于本地jar包)。
2)将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
3)在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。
认证
认证包括以下几部分的认证:
1)文件格式验证:包括魔数、支持版本、常量类型、指向常量的索引的有效性、常量的编码等
2)元数据验证:类是否有父类、继承的合法性、非抽象类方法的完整性、方法合理性等
3)字节码验证:操作数栈语义合法性检查、虚拟机规定的类型转换校验等
4)符号引用验证:对类自身以外(常量池中的各种符号引用)的信息进行匹配性校验,包括类、方法、字段等的描述符
准备
准备阶段是正式为类变量分配内存并设置类变量(区别于实例变量,指的是类的静态字段等)初始值的阶段。比如int、short、char、float、long等类型的初始值是0,boolean类型初始值是false,reference对象类型的初始值是null。
解析
解析阶段是虚拟机将常量池内的符号引用(Class以CONSTANT_Class_info、CONSTANT_Fieldref_info、CONSTANT_Methodref_info、CONSTANT_InterfaceMethodref_info、CONSTANT_MethodType_info、CONSTANT_MethodHandle_info、CONSTANT_InvokeDynamic_info7种类符号引用结构描述)替换为直接引用(直接指向目标的指针、相对偏移量或一个能间接定位到目标的句柄)的过程。只需要保证使用的符号使用之前被解析就可以,可以先于初始化。
除了与invokedynamic指令相关的类,其他类被解析后都会被缓存,避免解析动作的重复进行。
初始化
类初始化阶段是类加载的最后一个阶段。前面的类加载过程中,除了在加载阶段用户应用程序可以通过自定义类加载器参与之外,其余动作完全由虚拟机主导和控制。到了初始化阶段,才真正开始执行类中定义的java代码。
初始化阶段是执行类构造器<clinit>()方法的过程。<clinit>()方法是由编译器自动收集所有类变量的赋值动作和静态语句块(static{}块)中的语句合并产生的,编译器收集的顺序是由语句在源文件中出现的顺序所决定的,它不需要显示地调用父类构造器。
类加载器
对于任何一个类,都需要由加载它的类加载器和这个类本身一同确立其在java虚拟机中的唯一性,每一个类加载器,都拥有一个独立的类名称空间。不同类加载器加载的同一个类不相等。
虚拟机系统提供3种的类加载器:Bootstrap ClassLoader、Extension ClassLoader、Application ClassLoader
Bootstrap ClassLoader
启动类加载器,一般由C++实现,是虚拟机的一部分。该类加载器主要职责是将JAVA_HOME路径下的\lib目录中能被虚拟机识别的类库(比如rt.jar)加载到虚拟机内存中。Java程序无法直接引用该类加载器
Extension ClassLoader
扩展类加载器,由Java实现,独立于虚拟机的外部。该类加载器主要职责将JAVA_HOME路径下的\lib\ext目录中的所有类库,开发者可直接使用扩展类加载器。 该加载器是由sun.misc.Launcher$ExtClassLoader实现。
Application ClassLoader
应用程序类加载器,该加载器是由sun.misc.Launcher$AppClassLoader实现,该类加载器负责加载用户类路径上所指定的类库。开发者可通过ClassLoader.getSystemClassLoader()方法直接获取,故又称为系统类加载器。当应用程序没有自定义类加载器时,默认采用该类加载器。
双亲委派模型
如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父加载器去完成,每一个层次的类加载器都是如此。只有当父加载器反馈自己无法完成这个加载请求时,子加载器才会尝试自己去加载。