Java 入门指南:JVM(Java虚拟机)—— 双亲委派模型(Parent Delegation Model)

ops/2024/9/23 15:30:43/

文章目录

    • 双亲委派模型
      • 执行流程
        • 示例流程
      • 双亲委派模型的优势
      • 打破双亲委派模型
    • 总结

双亲委派模型

双亲委派模型(Parent Delegation Model)是 Java 类加载机制中的一个核心概念,它用于组织和管理 Java 类加载器的工作方式。在Java中,类加载器负责将 .class 文件加载到内存中,并通过双亲委派模型来保证类的加载过程具有一定的安全性和统一性。

这一模型的核心思想是:类加载器在接收到类加载请求时,首先不会自己尝试加载该类,而是将这个请求委派给它的父类加载器去处理。只有当父类加载器无法加载该类时,当前类加载器才会尝试自己去加载

根据官方的说明:

ClassLoader使用委托模型来搜索类和资源。每个 ClassLoader 实例都有一个相关的父类加载器。需要查找类或资源时,ClassLoader 实例会在试图亲自查找类或资源之前,将搜索类或资源的任务委托给其父类加载器。

虚拟机中被称为 “bootstrap class loader” 的内置类加载器本身没有父类加载器,但是可以作为 ClassLoader 实例的父类加载器。

这里的双亲更多地表达的是“父母这一辈”的人,并不是说真的有一个 MotherClassLoader 和一个 FatherClassLoader

另外,类加载器之间的父子关系一般不是以继承的关系来实现的,而是通常使用组合关系来复用父加载器的代码。在面向对象编程中,有一条非常经典的设计原则:组合优于继承,多用组合少用继承。

java">public abstract class ClassLoader {...// 组合private final ClassLoader parent;protected ClassLoader(ClassLoader parent) {this(checkCreateClassLoader(), parent);}...
}

执行流程

双亲委派模型的实现代码的逻辑非常清晰,都集中在 java.lang.ClassLoaderloadClass() 中,以下是 loadClass() 的源代码:

java">protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException
{synchronized (getClassLoadingLock(name)) {//首先,检查该类是否已经加载过Class c = findLoadedClass(name);if (c == null) {//如果 c 为 null,则说明该类没有被加载过long t0 = System.nanoTime();try {if (parent != null) {//当父类的加载器不为空,则通过父类的loadClass来加载该类c = parent.loadClass(name, false);} else {//当父类的加载器为空,则调用启动类加载器来加载该类c = findBootstrapClassOrNull(name);}} catch (ClassNotFoundException e) {//非空父类的类加载器无法找到相应的类,则抛出异常}if (c == null) {//当父类加载器无法加载时,则调用findClass方法来加载该类//用户可通过覆写该方法,来自定义类加载器long t1 = System.nanoTime();c = findClass(name);//用于统计类加载器相关的信息sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}}if (resolve) {//对类进行link操作resolveClass(c);}return c;}
}

每当一个类加载器接收到加载请求时,它会先将请求转发给父类加载器。在父类加载器没有找到所请求的类的情况下,该类加载器才会尝试去加载。

  1. 检查类是否已被加载:当一个类加载器接收到类加载请求时,它首先会检查该类是否已经被加载过。如果已经加载过,就直接返回该类的 Class 对象,避免重复加载。

  2. 委派给父类加载器:如果该类尚未被加载,类加载器就会将加载请求委派给它的父类加载器。这个过程会一直进行,直到达到顶层的启动类加载器(Bootstrap ClassLoader)。

  3. 父类加载器尝试加载:父类加载器在接收到加载请求后,会尝试在自己的搜索范围内加载该类。如果找到了相应的类文件,就加载并返回该类的 Class 对象;如果没有找到,就将请求继续向上委派。

  4. 子类加载器尝试加载:如果所有的父类加载器都无法加载该类,那么最初的类加载器就会尝试自己去加载该类。如果它也无法加载,就会抛出 ClassNotFoundException 异常。

示例流程

假设应用程序类加载器收到了加载 com.example.MyClass 的请求:

  1. 应用程序类加载器(App ClassLoader)首先会把请求交给它的父类加载器—— 扩展类加载器(Extension ClassLoader)。

  2. 扩展类加载器(Extension ClassLoader)再将请求交给它的父类加载器—— 启动类加载器(Bootstrap ClassLoader)。

  3. 启动类加载器(Bootstrap ClassLoader) 查找它负责的 rt.jar 等核心库,如果没有找到 com.example.MyClass,那么它会将控制权返回给 扩展类加载器(Extension ClassLoader)。

  4. 扩展类加载器(Extension ClassLoader)检查自己负责的扩展目录下的 jar 包,如果没有找到,再将控制权返回给 应用程序类加载器(App ClassLoader)。

  5. 应用程序类加载器(App ClassLoader)最后检查应用程序的 classpath 目录,如果找到了 com.example.MyClass,则加载该类。

双亲委派模型的优势

  1. 保证类的唯一性和稳定性:通过双亲委派模型,可以确保一个类在Java虚拟机中只被加载一次,避免了类的重复加载和版本冲突问题。

  2. 保护核心类库的安全:由于核心Java类库(如java.lang和java.util包中的类)总是由启动类加载器加载,因此可以确保这些类库的安全性和完整性,防止被恶意代码篡改。

  3. 提高类加载的效率:通过委派机制,类加载器可以复用父类加载器已经加载的类,从而减少重复加载的开销。

打破双亲委派模型

尽管双亲委派模型有许多优点,但它也有一些局限性,特别是在需要动态加载类或者需要实现类的热更新的情况下。例如,在 OSGi(Open Service Gateway Initiative)框架中,这种模型可能会限制模块之间的隔离性。因此,在某些特定的应用场景中,可能会对双亲委派模型进行一定的调整或使用替代方案。

因此在某些特定场景下,可能需要打破双亲委派模型。

在 Java 入门指南:JVM(Java虚拟机)—— Java 类加载器详解 中,如果我们需要自定义加载器,并想打破双亲委派模型则需要重写 loadClass() 方法。因为类加载器在进行类加载的时候,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成(调用父加载器 loadClass() 方法来加载类)。

重写 loadClass()方法之后,我们就可以改变传统双亲委派模型的执行流程。例如,子类加载器可以在委派给父类加载器之前,先自己尝试加载这个类,或者在父类加载器返回之后,再尝试从其他地方加载这个类。具体的规则由我们自己实现,根据项目需求定制化。

我们比较熟悉的 Tomcat 服务器为了能够优先加载 Web 应用目录下的类,然后再加载其他目录下的类,就自定义了类加载器 WebAppClassLoader 来打破双亲委托机制。

每个 Web 应用都会创建一个单独的 WebAppClassLoader,并在启动 Web 应用的线程里设置线程线程上下文类加载器为 WebAppClassLoader。各个 WebAppClassLoader 实例之间相互隔离,进而实现 Web 应用之间的类隔。

而 Tomcat 中另一个自定义的类加载器 SharedClassLoader 作为 WebAppClassLoader 的父加载器,专门来加载 Web 应用之间共享的类比如 Spring、Mybatis。

总结

双亲委派模型是 Java 类加载机制中的一个重要概念,它通过委派机制保证了类的唯一性和安全性。然而,在特定场景下,为了满足特定的需求,可能需要打破这一模型。因此,在设计和实现Java应用程序时,需要根据实际情况选择合适的类加载策略。


http://www.ppmy.cn/ops/111622.html

相关文章

机器学习--逻辑回归

逻辑回归 前情提要&#xff1a;线性回归 关于分类 C l a s s i f i c a t i o n Classification Classification 在逻辑回归中&#xff0c;我们只讨论 y ∈ { 0 , 1 } y\in\{0, 1\} y∈{0,1} 的情况。其中 1 1 1 表示 p o s i t i v e c l a s s positive \; class posit…

如何进入电脑BIOS

前言 在日常使用电脑的过程中&#xff0c;有时我们需要进入BIOS&#xff08;基本输入输出系统&#xff09;来调整设置&#xff0c;比如更改启动顺序、调整系统日期时间或是优化硬件配置。BIOS是计算机启动时最先运行的程序之一&#xff0c;它位于主板上的一个ROM芯片中。下面&…

R与机器学习系列|15.可解释的机器学习算法(Interpretable Machine Learning)(下)

今天我们介绍可解释机器学习算法的最后一部分&#xff0c;基于XGBoost算法的SHAP值可视化。关于SHAP值其实我们之前的很多个推文中都介绍到&#xff0c;不论是R版本的还是Python版本的&#xff0c;亦不论是普通的分类问题还是生存数据模型的。在此推文中我们将基于XGBoost模型理…

华为VRP系统基本操作

简介&#xff1a; VRP是Versatile Routing Platform的简称&#xff0c;它是华为公司数据通信产品的通用网络操 作系统。目前&#xff0c;在全球各地的网络通信系统中&#xff0c;华为设备几乎无处不在&#xff0c;因此&#xff0c;学习了 解VRP的相关知识对于网络通信技术人员来…

EmguCV学习笔记 VB.Net 11.5 目标检测

版权声明&#xff1a;本文为博主原创文章&#xff0c;转载请在显著位置标明本文出处以及作者网名&#xff0c;未经作者允许不得用于商业目的。 EmguCV是一个基于OpenCV的开源免费的跨平台计算机视觉库,它向C#和VB.NET开发者提供了OpenCV库的大部分功能。 教程VB.net版本请访问…

【智路】智路OS Airos Edge 2.0 Quick Start

Airos Edge 2.0 Quick Start 1 智路OS2.0 1.1 简介 智路OS路侧操作系统airos-edge自下而上分别由内核层&#xff0c;硬件抽象层、框架层、服务层和应用层构成&#xff1b;提供了一系列抽象和框架&#xff0c;支持设备接入、服务、应用等组件开发&#xff0c;兼容X86和ARM操作…

力扣刷题(6)

两数之和 II - 输入有序数组 两数之和 II - 输入有序数组-力扣 思路&#xff1a; 因为该数组是非递减顺序排列&#xff0c;因此可以设两个左右下标当左右下标的数相加大于target时&#xff0c;则表示右下标的数字过大&#xff0c;因此将右下标 - -当左右下标的数相加小于targ…

AMD ThinkSystem服务器上的 Linux 和 C 状态设置 - Lenovo ThinkSystem

受影响的配置 该系统可以是以下任何Lenovo服务器&#xff1a; ThinkSystem 、SR645&#xff08; ThinkSystem &#xff09;ThinkSystem &#xff0c;SR645 V3&#xff08; ThinkSystem &#xff09;ThinkSystem &#xff0c;SR635 V3&#xff08; ThinkSystem &#xff09;Th…