图片来自网络。
虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的java类型,这就是虚拟机的类加载机制。
1. 类加载的时机
类从被虚拟机加载到内存,到卸载出内存为止,他的整个生命周期为:加载、验证、准备、解析、初始化、使用、卸载七个过程,其中验证、准备、解析统称为连接。
2. 加载
加载是类加载过程的第一个阶段,在这个阶段,虚拟机主要完成三件事情:
- 通过一个类的全限定名来获取定义此类的二进制流
- 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
- 在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口
获取二进制流的方法有以下几种:
- 从zip包中读取,这很常见,最终成为日后jar、ear、war等各式的基础
- 从网络中获取,applet
- 运行时动态生成,最常见的就是动态代理
- 由其他文件生成,如jsp对应生成class类
- 从数据库中读取,情景较少
3. 验证
验证是连接的第一步,这一阶段的目的是为了确保class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会威胁虚拟机自身的安全。验证阶段会完成四个阶段的校验动作:文件格式、元数据、字节码、符号引用。
3.1 文件格式校验
是否符合class文件格式的规范,并且能被当前版本的虚拟机处理。
- 是否以模式0xCAFEBABE(咖啡baby)开头
- 主次版本号是否在当前虚拟机的处理范围之内
- 常量池的常量中是否有不被支持的常量类型
- 指定常量的各种索引中是狗友指向不存在的常量或者不支持的常量类型
- CONSTANT_Utf8_info 型的常量中是狗友不符合utf8编码的常量
- class文件中各个文件本身是否有被删除的或附加的其他信息
3.2 元数据验证
对字节码描述的信息进行语义分析
- 这个类是否有父类,object类除外
- 这个类的父类是否继承了不允许被继承的类,final
- 如果这个类不是抽象类,是否实现了其父类或者接口中所有要求实现的方法
- 类中的字段、方法是否与父类产生矛盾
3.3 字节码验证
通过数据流河控制流分析,确定程序语义是否合法的、符合逻辑的,对类的方法体进行校验分析,保证被校验类的方法在运行时不回做出危害虚拟机安全的事件,如:
- 保证任意时刻操作数栈的数据结构累心与指令代码序列都能配合工作
- 保证跳指令不回跳转到方法体以外的字节码指令上
- 保证方法体中的类型转换时有效的
3.4 符号引用类型
- 符号饮用中通过字符串描述的全限定名是否能找到对应的类
- 在指定类中是否存在符合方法的字段描述以及简单名称所描述的方法和字段
- 符号引用的类、字段、方法的访问性是否可以被当前类访问。
4. 准备
准备阶段是正式为类变量分配内存并设置类变量的初始值的阶段,这些变量所使用的内存都将在方法区中进行分配,这里的分配只包含类变量,被final修饰的变量,而不包括实例变量。
5. 解析
解析阶段是虚拟机将常量池内的符号引用替换为之内引用的过程。
- 类或接口的解析
- 字段解析
- 类方法解析
- 接口方法解析
6. 初始化
在初始化的时候,静态域的初始化和静态代码块的执行会从上到下依次执行
7. 双亲委托机制
如果一个类收到类加载的请求,他首先不回自己去尝试加载这个类,而是这个请求委托给父类加载器去完成,只有父类反馈无法完成的时候,才会自己去加载。
类加载机制很重要,很重要,很重要。