类加载流程
概述
类加载器的目的
- 通过将编译后的字节码文件的加载,生成某种形式的与Class相关的数据结构存入内存,程序可以通过这个数据结构生成对应的object对象。这个过程是在运行时进行的,这也是java实现动态拓展的根基。
类的生命周期:
- javac编译为字节码文件
- 加载:在方法区中生成静态数据结构、在堆中生成便于用户调用的
java.lang.Class
类型的对象 - 链接:验证、准备、解析(注意解析是可以在初始化之后进行的,实现后期绑定,其他过程顺序不可变)
- 初始化
- 使用
- 卸载
加载
加载是整个类加载过程的第一个环节,它的主要目的在于通过.class
的二进制流,在方法区中生成一个静态的数据结构,并在堆中生成一个java.lang.Class
类型的对象以方便用户调用生成对应的实例化对象的过程。
这是一个但输入双输出的模型:
- 输入:
.class
的二进制流,可以是本地文件、网络二进制流、数据库二进制流,甚至是计算出来的class
,动态代理技术就是基于此,生成代理对象 - **输出:**将二进制流的静态存储结构转换为方法区中的运行时存储数据结构
- **输出:**可以被调用的
java.lang.Class
类型的对象,可以通过这个对象生成相应的object对象
链接
验证
总而言之就是对Class进行语法语义分析,保证其不危害虚拟机
- 文件格式验证:验证字节流是否符合规范
- 元数据验证:类的继承关系是否准确等
- 字节码验证:确定程序语义正确
- 符号引用验证:确定程序能正确运行
准备
为静态变量分类内存并赋0值,分配的内存在方法区。但如果是final关键字修饰的基本类型,那么就在准备阶段就赋值
解析
将符号引用转换为直接引用的过程。假设A引用了B,那么A中B的地址采用的是一个字符串进行存储,当加载A发现B没有被加载的时候会触发B的加载,并将字符串替换为B的地址,这就是一个符号引用转换为直接引用 的过程
- 静态绑定:如果引用的是具体的实现类,能实现明确的直接引用。
- 动态绑定:如果引用的是抽象类或者接口,接口可能有多个具体的实现类,无法明确具体的地址,那么就等一等,一直到运行过程中发生了调用,确定具体引用哪个实现类。这也是为什么解析阶段可以发生在初始化之后。
初始化
代码中是否有主动初始化变量的过程,有的话就执行。
卸载
自带类加载器不会被卸载,自定义的类加载器加载的类是可以被卸载的,满足三个条件
- 该类所有实例被GC
- 该类没有再被引用
- 该类的类加载器的实例已经被GC