avatar

目录
Java类加载和双亲委派模型

在Java中, 实现通过一个类的全限定名获取描述该类的二进制字节流这样动作的代码被称为类加载器 (Class Loader)

1. 概述

对于每一个Java class文件而言,它需要被JVM加载到内存中才可以开始解释执行其中的字节码。将字节码文件加载到内存中这一过程称为 类加载 (Class Loading) . 一个类从被加载到 JVM 内存中开始到卸载出内存为止,其整个生命周期将会经历 加载 (Loading), 验证(Verification),准备(Preparation),解析(Resolution),初始化(Initialization),使用(Using) 和 卸载(Unloading) 七个阶段。这些阶段的发生顺序如下图所示:

类加载阶段顺序
  • 加载

加载是整个过程中的第一个阶段,主要完成以下三件事:

  1. 通过一个类的全限定名来获取定义此类的二进制字节流,通常可以从zip包,jar包,war包或者网络中读取到类的二进制字节流。
  2. 将这个字节流代表的静态存储结构转化为方法区的运行时数据结构
  3. 在内存中生成一个代表此类的Class对象,作为方法区中这个类的各种数据的访问入口
  • 验证

验证是链接阶段的第一步,其目的是确保Class文件字节流中包含的信息符号符合《Java虚拟机规范》的全部约束要求。验证包含以下几个步骤:

  1. 文件格式验证
  2. 元数据验证,主要是对类的元数据信息进行语义分析
  3. 字节码验证,这一步主要的目的是通过数据流分析和控制流分析,确定程序语义是合法的、合乎逻辑的。
  4. 符号引用验证,最后这一步校验行为发生在JVM将符号引用转化为直接引用的时候,这个动作将在接下来的解析阶段中发生。
  • 准备

准备阶段是正式为类中定义的变量(即 static 修饰的变量)分配内存并设置类变量初始值的阶段。需要着重强调的一点是,在这个时候进行的内存分配仅包括类变量,不包括实例变量。

  • 解析

解析阶段是Java虚拟机将常量池内的符号引用替换为直接引用的过程。

  • 初始化

类的初始化是类加载过程的最后一个步骤。在初始化阶段,JVM 才真正开始执行字节码文件中的程序代码,将主导权交由应用程序。初始化阶段就是执行类构造器<clinit>() 的过程,<clinit>()方法是 Javac 编译器的自动生成物。

2. 类加载器

类加载器用于实现类的加载动作,但是对于 Java 程序而言,类加载器的作用又绝不仅止于此!对于任意一个类,都必须由它的类加载器和这个类本身一起共同确立其在 JVM 中的唯一性,每一个类加载器,都拥有一个独立的类名称空间。换句话讲,在 JVM 中,判断两个类是否相等的前提条件是这两个类必须是由同一个类加载器所加载的。否则的话,即使这两个类来自于同一份Class文件,但是加载它们的类加载器不同,则这两个类必定是不相等的。

2.1 三大类加载器

JDK 1.8之前,绝大多数Java 程序都使用系统所提供的以下三个类加载器来进行加载。

  1. 启动类加载器Bootstrap Class Loader): 负责加载存放在<JAVA_HOME>/lib 目录下或者是被 -Xbootclasspath 参数指定路径下能够被JVM所识别的类,如 rt.jar、 tools.jar 等等

  2. 扩展类加载器Extension Class Loader):这个类加载器是在类sun.misc.Launcher$ExtClassLoader

    中以代码的形式实现的,负责加载<JAVA_HOME>/lib/ext 目录中、或者是被java.ext.dirs 系统变量所指定的路径中所有类库。

  3. 应用程序类加载器App Class Loader):应用程序的类加载器是ClassLoader类中的getSystemClassLoader()方法的返回值,由sun.misc.Launcher$AppClassLoader负载加载用户类路径上,也就是 classpath上的所有类库。

2.2 双亲委派模型

各种类加载器之间的层次关系被称为双亲委派模型Parents Delegation Model)。 这个模型要求除了最顶层的启动类加载器之外,其余的类加载器都应该有父 类加载器。

双亲委派模型

双亲委派模型的工作过程为:如果一个类加载器接收到了加载类的请求,它首先不会自己去尝试去加载这个类,而是把这个请求委派给父类加载器去完成。每一个层次的类加载器都是如此!由此可见,所有的加载请求最终都应该被传送到最顶层的启动类加载器中,只有当父类加载器反馈说自己无法完成加载时,子加载器才会尝试自己去完成加载。

此外,特别需要注意的是:JDK 9 之后由于引入了模块化技术,类加载器架构中的内容发生了一些变化。主要体现为扩展类加载器平台类加载器Platform Class Loader)所取代,这是因为从JDK 9开始基于模块化进行构建,把原来的 rt.jartools.jar 拆分成了数十个 JMOD 文件,其中的Java类库已经天然地满足了可扩展的需求,无需再保留<JAVA_HOME>/lib/ext 目录了。而且又为了兼容从JDK1.2起就采用的三层类加载器架构和双亲委派模型,仅仅是将双亲委派模型图中的扩展类加载器这一层替换为了平台类加载器。另外还有一点便是,从 JDK 9开始,平台类加载器和应用程序类加载器都不再派生自 java.net.URLClassLoader. 现在是,启动类加载器、平台类加载器和应用程序类加载器全部继承于 jdk.internal.loaderBuiltinClassLoader.


文章封面图来源于网络,侵删

文章作者: JanGin
文章链接: http://jangin.github.io/2021/04/17/java-class-loader-and-parents-delegation-model/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 JanGin's BLOG

评论