浅析类加载

37 阅读3分钟

  在 Java 中,类加载是将类从文件系统或其他地方加载到内存中的过程。Java 采用了延迟加载的方式,只有在需要使用某个类时,JVM(Java 虚拟机)才会加载它。

类加载的过程

image.png

类整个生命周期可以概括上图所示过程:加载、验证、准备、解析、初始化、使用和卸载。其中,验证、准备和解析这三个阶段可以统称为连接

  • 加载

  类加载器首先会通过类的全限定名(例如 com.example.MyClass)查找该类对应的 .class 文件,将文件中的二进制数据读入到内存中,其静态存储结构转化为方法区的运行时数据结构,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构。

  • 连接

    • 验证   验证的主要作用就是确保被加载的类的正确性,止恶意代码或不合法的类文件进入 JVM。会进行文件格式、元数据、字节码、符号引用验证
    • 准备   该阶段为类的静态变量分配内存并设置初始值。初始化的仅仅是类变量(static),并给其对应类型的默认零值(如00Lnullfalse等)。但如果该变量同时还被final修饰,那其在该阶段的初始值为实际的值,而不是零值。
    • 解析   将类中的符号引用(比如方法和变量的名字)转化成可以直接访问的内存地址。
  • 初始化

  在类的初始化阶段,JVM 会执行类的静态初始化块(static {})以及静态字段的初始化。如果类包含静态代码块或静态变量,它们将在初始化阶段被执行和赋值。

  在上述的五个阶段中,加载、验证、准备和初始化这四个阶段发生的顺序是确定的,而解析阶段则不一定,它在某些情况下可以在初始化阶段之后开始。

类加载器

  类加载是由 类加载器(ClassLoader) 实现的。

Java 三种常见的类加载器:

  • 启动类加载器(Bootstrap ClassLoader)

  启动类加载器是 JVM 自带的,它负责加载 Java 核心类库(比如 java.lang.String)。启动类加载器通常是用 C 或 C++ 编写的。

  • 扩展类加载器(Extension ClassLoader)

  扩展类加载器负责加载 Java 扩展库,通常是 jre/lib/ext/ 目录中的类库。它的父类加载器是启动类加载器。

  • 应用类加载器(AppClassLoader)

  应用类加载器是最常用的加载器,它负责加载你应用程序中的类(比如 JAR 包中的类)。它的父类加载器是扩展类加载器。

双亲委派模型

  如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求时,子加载器才会尝试自己去加载。

image.png

大致执行过程:

  • 当AppClassLoader加载一个class时,它首先不会自己去尝试加载这个类,而是把类加载请求委派给父类加载器ExtClassLoader去完成。

  • 当ExtClassLoader加载一个class时,它首先也不会自己去尝试加载这个类,而是把类加载请求委派给BootStrapClassLoader去完成。

  • 如果BootStrapClassLoader加载失败(例如在$JAVA_HOME/jre/lib里未查找到该class),会使用ExtClassLoader来尝试加载;

  • 若ExtClassLoader也加载失败,则会使用AppClassLoader来加载,如果AppClassLoader也加载失败,则会报出异常ClassNotFoundException。