# 虚拟机类加载机制
类加载时机
类从被加载到虚拟机内存中开始,到卸载出内存位置,整个生命周期如下
- 加载
- 验证
- 准备
- 解析
- 初始化
- 使用
- 卸载
必须初始化的情况
- 遇到
new
、getstatic
、putstatic
、invokestatic
字节码指令的时候 - 使用
java.lang.reflect
包的方法对类进行反射调用的时候 - 当一个类初始化的时候,发现其父类还没有进行初始化,则需要先触发父类的初始化(接口只有在使用的时候才会对父类进行初始化)
- 当虚拟机启动时,用户指定一个要执行的主类(包含main的类)
- 使用JDK1.7的动态语言支持时
类加载全过程
类加载过程包括:加载、验证、准备、解析、初始化
加载阶段的任务
- 通过一个类的全限定名来获取定义此类的二进制字节流
- 将这个字节流所代表的静态数据结构转化为方法区的运行时数据结构
- 在内存中生成一个代表这个类的
java.lang.Class
对象,作为方法区这个类的各种数据的访问入口
数组加载过程,数组本身不是通过类加载器创建,而是由Java虚拟机直接创建
- 如果数组的组件类型(去掉一个维度之后的类型)是引用类型,则调用上述方式加载该组件类型,数组将在该组件类型的类加载器的类名称空间上被标识
- 如果数组的组件类型不是引用类型(基本类型),Java虚拟机将把数组标记为与引导类加载器关联
- 数组类的可见性与它的组件类型的可见性一直,如果不是引用类型,则可见性默认为public
验证,连接阶段的第一步,确保Class文件中的字节流包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全
准备,正式为类变量分配内存并设置类变量初始化值的阶段(静态(static修饰的)变量,并且只是初始化为零值,如果是常量,则直接初始化为其值)
解析,将常量池内的符号引用替换为直接引用的过程
初始化,为类生成方法,用于执行类变量以及static代码块中的操作,操作顺序由字节码中定义的顺序决定
类加载器
对于任意一个类,都需要由加载它的类加载器和这个类本身一同确定其在Java虚拟机中的唯一性,每一个类加载器,都拥有一个独立的类名称空间,也就是说,判断两个类是否相等,只有当这两个类是由同一个类加载器加载的前提下才有意义,否则,即使是来自同一个Class文件,被同一个虚拟机加载,只要加载器不同,则两个类就不同
类加载器类型
- 启动类加载器,Bootstrap ClassLoader,负责加载/lib目录中的,或者被-Xbootclasspath参数所指定的路径中,并且是虚拟机识别的,如rt.jar,无法被Java程序直接引用,使用c++编写
- 扩展类加载器,Extension ClassLoader,负责加载/lib/ext目录中,或者被java.ext.dirs系统变量所指定的路径中的所有类库,开发者可以直接使用扩展类加载器
- 应用程序类加载器,Application ClassLoader,也称为系统类加载器,负责加载用户类路径上所指定的类库,开发者可以直接使用这个类加载器,默认的类加载器
双亲委派模型(Parent Delegation Model)
- 除了顶层的启动类加载器外,其余的类加载器都应当有自己的父类加载器
- 工作过程
- 如果一个类加载器收到类加载的请求,首先不会自己去尝试加载这个类,而是把这个请求委托给父类加载器去完成,每一个层次都是如此,因此,所有的类加载请求最终都应该传送到顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求时,子加载器才会尝试自己去加载
- 好处
- Java类随着它的类加载器一起具备了一种优先级的层次关系,保证程序的稳定运行