Java双亲委派机制

Java双亲委派机制1 什么是双亲委派 虚拟机在加载类的过程中需要使用类加载器进行加载 而在 Java 中 类加载器有很多 那么当 JVM 想要加载一个 class 文件的时候 到底应该由哪个类加载器加载呢 这就不得不提到 双亲委派机制 首先 我们需要知道的是 Java 语言系统中支持以下 4 种类加载器 BootstrapCla 启动类加载器 ExtentionCla 标准扩展类加载器 ApplicationC 应用类加载器 UserClassLoa 用户自定义类加

1、什么是双亲委派?

这四种类加载器之间,是存在着一种层次关系的,如下图:

在这里插入图片描述

一般认为上一层加载器是下一层加载器的父加载器,那么,除了BootstrapClassLoader之外,所有的加载器都是有父加载器的。

那么,所谓的双亲委派机制,指的就是:当一个类加载器收到了类加载的请求的时候,他不会直接去加载指定的类,而是把这个请求委托给自己的父加载器去加载。只有父加载器无法加载这个类的时候,才会由当前这个加载器来负责类的加载。

  • Bootstrap ClassLoader ,主要负责加载Java核心类库,%JRE_HOME%\lib下的rt.jar、resources.jar、charsets.jar和class等。
  • Extention ClassLoader,主要负责加载目录%JRE_HOME%\lib\ext目录下的jar包和class文件。
  • Application ClassLoader ,主要负责加载当前应用的classpath下的所有类
  • User ClassLoader , 用户自定义的类加载器,可加载指定路径的class文件

那么也就是说,一个用户自定义的类,如com.hollis.ClassHollis 是无论如何也不会被Bootstrap和Extention加载器加载的。

2、为什么需要双亲委派?

因为类加载器之间有严格的层次关系,那么也就使得Java类也随之具备了层次关系。或者说这种层次关系是优先级。

比如一个定义在java.lang包下的类,因为它被存放在rt.jar之中,所以在被加载过程汇总,会被一直委托到Bootstrap ClassLoader,最终由Bootstrap ClassLoader所加载。

而一个用户自定义的com.hollis.ClassHollis类,他也会被一直委托到Bootstrap ClassLoader,但是因为Bootstrap ClassLoader不负责加载该类,那么会在由Extention ClassLoader尝试加载,而Extention ClassLoader也不负责这个类的加载,最终才会被Application ClassLoader加载。

这种机制有几个好处:

  • 首先,通过委派的方式,可以避免类的重复加载,当父加载器已经加载过某一个类时,子加载器就不会再重新加载这个类。
  • 另外,通过双亲委派的方式,还保证了安全性。因为Bootstrap ClassLoader在加载的时候,只会加载JAVA_HOME中的jar包里面的类,如java.lang.Integer,那么这个类是不会被随意替换的,除非有人跑到你的机器上, 破坏你的JDK。那么,就可以避免有人自定义一个有破坏功能的java.lang.Integer被加载。这样可以有效的防止核心Java API被篡改。

3、”父子加载器”之间的关系是继承吗?

很多人看到父加载器、子加载器这样的名字,就会认为Java中的类加载器之间存在着继承关系。

这里需要明确一下,双亲委派模型中,类加载器之间的父子关系一般不会以继承(Inheritance)的关系来实现,而是都使用组合(Composition)关系来复用父加载器的代码的。

如下为ClassLoader中父加载器的定义:

public abstract class ClassLoader { 
    // The parent class loader for delegation private final ClassLoader parent; } 

4、双亲委派是怎么实现的?

protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { 
    synchronized (getClassLoadingLock(name)) { 
    // First, check if the class has already been loaded Class<?> c = findLoadedClass(name); if (c == null) { 
    long t0 = System.nanoTime(); try { 
    if (parent != null) { 
    c = parent.loadClass(name, false); } else { 
    c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { 
    // ClassNotFoundException thrown if class not found // from the non-null parent class loader } if (c == null) { 
    // If still not found, then invoke findClass in order // to find the class. long t1 = System.nanoTime(); c = findClass(name); // this is the defining class loader; record the stats sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } } if (resolve) { 
    resolveClass(c); } return c; } } 

代码不难理解,主要就是以下几个步骤:

  1. 先检查类是否已经被加载过 ;
  2. 若没有加载则调用父加载器的loadClass()方法进行加载 ;
  3. 若父加载器为空则默认使用启动类加载器作为父加载器 ;
  4. 如果父类加载失败,抛出ClassNotFoundException异常后,再调用自己的findClass()方法进行加载。

5、如何主动破坏双亲委派机制?

知道了双亲委派模型的实现,那么想要破坏双亲委派机制就很简单了。

因为他的双亲委派过程都是在loadClass方法中实现的,那么想要破坏这种机制,那么就自定义一个类加载器,重写其中的loadClass方法,使其不进行双亲委派即可。

ClassLoader中和类加载有关的方法有很多,前面提到了loadClass,除此之外,还有findClass和defineClass等,那么这几个方法有什么区别呢?

  • loadClass():就是主要进行类加载的方法,默认的双亲委派机制就实现在这个方法中。
  • findClass():根据名称或位置加载.class字节码
  • definclass():把字节码转化为Class
 / * @since 1.2 */ protected Class<?> findClass(String name) throws ClassNotFoundException { 
    throw new ClassNotFoundException(name); } 

这个方法只抛出了一个异常,没有默认实现。

JDK1.2之后已不再提倡用户直接覆盖loadClass()方法,而是建议把自己的类加载逻辑实现到findClass()方法中。

因为在loadClass()方法的逻辑里,如果父类加载器加载失败,则会调用自己的findClass()方法来完成加载。

所以,如果你想定义一个自己的类加载器,并且要遵守双亲委派模型,那么可以继承ClassLoader,并且在findClass中实现你自己的加载逻辑即可。

6、双亲委派被破坏的例子

双亲委派机制的破坏不是什么稀奇的事情,很多框架、容器等都会破坏这种机制来实现某些功能。

  • 第一种被破坏的情况是在双亲委派出现之前。
    由于双亲委派模型是在JDK1.2之后才被引入的,而在这之前已经有用户自定义类加载器在用了。所以,这些是没有遵守双亲委派原则的。

  • 第二种,是JNDI、JDBC等需要加载SPI接口实现类的情况。
  • 第三种是为了实现热插拔热部署工具。为了让代码动态生效而无需重启,实现方式时把模块连同类加载器一起换掉就实现了代码的热替换。
  • 第四种时tomcat等web容器的出现。
  • 第五种时OSGI、Jigsaw等模块化技术的应用。

7、模块化技术与类加载机制

近几年模块化技术已经很成熟了,在JDK 9中已经应用了模块化的技术。

其实早在JDK 9之前,OSGI这种框架已经是模块化的了,而OSGI之所以能够实现模块热插拔和模块内部可见性的精准控制都归结于其特殊的类加载机制,加载器之间的关系不再是双亲委派模型的树状结构,而是发展成复杂的网状结构。

在这里插入图片描述

在JDK中,双亲委派也不是绝对的了。

在JDK9中,整个JDK都基于模块化进行构建,以前的rt.jar, tool.jar被拆分成数十个模块,编译的时候只编译实际用到的模块,同时各个类加载器各司其职,只加载自己负责的模块。

Class<?> c = findLoadedClass(cn); if (c == null) { 
    // 找到当前类属于哪个模块 LoadedModule loadedModule = findLoadedModule(cn); if (loadedModule != null) { 
    //获取当前模块的类加载器 BuiltinClassLoader loader = loadedModule.loader(); //进行类加载 c = findClassInModuleOrNull(loadedModule, cn); } else { 
    // 找不到模块信息才会进行双亲委派 if (parent != null) { 
    c = parent.loadClassOrNull(cn); } } } 
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/224303.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月17日 下午12:14
下一篇 2026年3月17日 下午12:14


相关推荐

  • 随机梯度下降法概述与实例分析_梯度下降法推导

    随机梯度下降法概述与实例分析_梯度下降法推导机器学习算法中回归算法有很多,例如神经网络回归算法、蚁群回归算法,支持向量机回归算法等,其中也包括本篇文章要讲述的梯度下降算法,本篇文章将主要讲解其基本原理以及基于SparkMLlib进行实例示范,不足之处请多多指教。梯度下降算法包含多种不同的算法,有批量梯度算法,随机梯度算法,折中梯度算法等等。对于随机梯度下降算法而言,它通过不停的判断和选择当前目标下最优的路径,从而能够在最短路径…

    2025年10月20日
    5
  • IDEA最全快捷键

    IDEA最全快捷键文章目录 IDEA 最全快捷键 IDEA 最全快捷键 Ctrl 快捷键 介绍 Ctrl F 在当前文件进行文本查找 必备 Ctrl R 在当前文件进行文本替换 必备 Ctrl Z 撤销 必备 Ctrl Y 删除光标所在行或删除选中的行 必备 Ctrl X 剪切光标所在行或剪切选择内容 Ctrl C 复制光标所在行或复制选择内容 Ctrl D 复制光标所在行或复制选择内容 并把复制内容插入光标位置下面 必备 Ctrl W 递进式选择代码块 可

    2026年3月27日
    3
  • 腾讯“龙虾”(OpenClaw/本地虾)对绿联科技是明确利好,且是强催化。一、先

    腾讯“龙虾”(OpenClaw/本地虾)对绿联科技是明确利好,且是强催化。一、先

    2026年3月15日
    3
  • onpropertychange属性

          当一个input标签的value通过键盘改变后失去焦点的时候,我们可以用onchange捕获事件,IE和FireFox都有效。而通过JavaScript来改变value的时候,onchange无法捕获事件,这时候我们可以用onpropertychange来捕获onpropertychange对于JavaScript和键盘对value的改变都有效。遗憾的是,onpropertychange事件只在IE中有效,FireFox中无效。尽FireFox的oninput可以来弥补(不必失去焦点),但是也仅

    2022年4月6日
    51
  • window安装memcache扩展

    window安装memcache扩展

    2022年2月23日
    52
  • string.hのstrcat的实现

    string.hのstrcat的实现

    2021年8月23日
    51

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号