类加载过程
加载 - 验证 - 准备 - 解析 - 初始化 - 使用 - 卸载
- 其中:验证、准备、解析 属于连接过程
- 为支持 Java 的运行时绑定,解析阶段有可能在初始化后才进行
加载
- 通过一个类的全限定名获取定义此类的二进制字节流
- 将这个字节流表示的静态存储结构转化为方法区的数据结构
- 在内存中生成该类的 class 对象,作为方法区访问这个类的入口
验证
为确保字节流包含的信息符合《Java虚拟机规范》
- 文件格式验证:魔数、版本、常量类型、编码检查等
- 元数据验证:类继承关系、权限验证
- 字节码验证:检查方法体,验证方法运行时不会危害 JVM
- 符号引用验证:引用的全限定类名是否存在、是否有权限访问
准备
正式加载类静态变量(分配内存、初始值,通常为0)
解析
将常量池内的符号引用替换为直接引用,主要针对:类或接口、字段、类方法、借口方法、方法类型、方法句柄、调用限定符
- 符号引用:用一组符号描述引用的目标,指向的目标可能尚未加载到 JVM 内存
- 直接引用:直接指向目标的指针,引用的目标一定在 JVM 内存
初始化
执行类构造器
<clinit>()
<cinit>()
方法由编译器生成,自动搜集类的初始化赋值、static 块语句产生的- 父类的
<cinit>()
方法一定先于子类执行 - JVM 确保
<cinit>()
方法在多个线程中正确的加锁同步,如果<cinit>()
中存在耗时操作,可能造成多个线程阻塞
类加载器
- 每个类加载器都有独立的名称空间,即使两个加载器加载了同一个字节码文件,得到的两个类仍然不相等。
双亲委派模型
- 启动类加载器(Bootstrap Class Loader)负责加载
JAVA_HOME/lib
或者-Xbootclasspath
下的类库,名称必须符合规范 - 扩展类加载器(Extension Class Loader)负责加载
JAVA_HOME/lib/ext
下的扩展类库,JDK9 模块化后,这种可以机制被取代 - 应用程序类加载器(Application Class Loader)也叫系统类加载器,负责加载用户类路径下的类库,可以在程序中直接使用(
getSystemClassLoader()
) - 自定义类加载器:允许用户自定义加载器完成类的加载
工作过程
- 一个类收到了类的加载请求,首先委派给上级加载器完成,因此所有的加载请求都会委派到启动类加载器
- 父级加载器无法完成加载时,当前加载器才会尝试加载
主要参考:《深入理解 Java 虚拟机 第三版》