类加载的时机
类从被加载到虚拟机内存中开始 到卸载出内存为止 它的整个生命周期包括:加载(Loading) 验证(Verification) 准备(Preparation) 解析(Resolution) 初始化(Initialization) 使用(Using)和卸载(Unloading)7个阶段 其中验证 准备 解析3个部分统称为连接(Linking)
加载 验证 准备 初始化和卸载这5个阶段的顺序是确定的 类的加载过程必须按照这种顺序按部就班地开始 而解析阶段则不一定:它在某些情况下可以在初始化阶段之后再开始 这是为了支持Java语言的运行时绑定(也称为动态绑定或晚期绑定)
类加载的过程
1.加载
加载 是 类加载 (Class Loading)过程的一个阶段 在加载阶段 虚拟机需要完成以下3件事情:
1)通过一个类的全限定名来获取定义此类的二进制字节流
2)将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
3)在内存中生成一个代表这个类的java.lang.Class对象 作为方法区这个类的各种数据的访问入口
2.验证
验证是连接阶段的第一步 这一阶段的目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求 并且不会危害虚拟机自身的安全
3.准备
准备阶段是正式为类变量分配内存并设置类变量初始值的阶段 这些变量所使用的内存都将在方法区中进行分配
4.解析
解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程
符号引用(Symbolic References):符号引用以一组符号来描述所引用的目标 符号可以是任何形式的字面量 只要使用时能无歧义地定位到目标即可 符号引用与虚拟机实现的内存布局无关 引用的目标并不一定已经加载到内存中 各种虚拟机实现的内存布局可以各不相同 但是它们能接受的符号引用必须都是一致的 因为符号引用的字面量形式明确定义在Java虚拟机规范的Class文件格式中
直接引用(Direct References):直接引用可以是直接指向目标的指针 相对偏移量或是一个能间接定位到目标的句柄 直接引用是和虚拟机实现的内存布局相关的 同一个符号引用在不同虚拟机实例上翻译出来的直接引用一般不会相同 如果有了直接引用 那引用的目标必定已经在内存中存在
5.初始化
类初始化阶段是类加载过程的最后一步 前面的类加载过程中 除了在加载阶段用户应用程序可以通过自定义类加载器参与之外 其余动作完全由虚拟机主导和控制 到了初始化阶段 才真正开始执行类中定义的Java程序代码(或者说是字节码)
类加载器
虚拟机设计团队把类加载阶段中的 通过一个类的全限定名来获取描述此类的二进制字节流 这个动作放到Java虚拟机外部去实现 以便让应用程序自己决定如何去获取所需要的类 实现这个动作的代码模块称为 类加载器
双亲委派模型
从Java虚拟机的角度来讲 只存在两种不同的类加载器:一种是启动类加载器(Bootstrap ClassLoader) 这个类加载器使用C++语言实现 是虚拟机自身的一部分 另一种就是所有其他的类加载器 这些类加载器都由Java语言实现 独立于虚拟机外部 并且全都继承自抽象类java.lang.ClassLoader
双亲委派模型的工作过程是:如果一个类加载器收到了类加载的请求 它首先不会自己去尝试加载这个类 而是把这个请求委派给父类加载器去完成 每一个层次的类加载器都是如此 因此所有的加载请求最终都应该传送到顶层的启动类加载器中 只有当父加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时 子加载器才会尝试自己去加载