类加载过程

加载 - 验证 - 准备 - 解析 - 初始化 - 使用 - 卸载

  • 其中:验证、准备、解析 属于连接过程
  • 为支持 Java 的运行时绑定,解析阶段有可能在初始化后才进行

加载

  1. 通过一个类的全限定名获取定义此类的二进制字节流
  2. 将这个字节流表示的静态存储结构转化为方法区的数据结构
  3. 在内存中生成该类的 class 对象,作为方法区访问这个类的入口

验证

为确保字节流包含的信息符合《Java虚拟机规范》

  1. 文件格式验证:魔数、版本、常量类型、编码检查等
  2. 元数据验证:类继承关系、权限验证
  3. 字节码验证:检查方法体,验证方法运行时不会危害 JVM
  4. 符号引用验证:引用的全限定类名是否存在、是否有权限访问

准备

正式加载类静态变量(分配内存、初始值,通常为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()
  • 自定义类加载器:允许用户自定义加载器完成类的加载

classloader.png

工作过程

  1. 一个类收到了类的加载请求,首先委派给上级加载器完成,因此所有的加载请求都会委派到启动类加载器
  2. 父级加载器无法完成加载时,当前加载器才会尝试加载

主要参考:《深入理解 Java 虚拟机 第三版》