JVM--双亲委派模型

1.双亲委派模型

定义:

站在Java虚拟机的角度来看,只存在两种不同的类加载器:一种是启动类加载器(Bootstrap ClassLoader),这个类加载器使用C++语言实现,是虚拟机自身的一部分;另外一种就是其他所有的类加载器,这些类加载器都由Java语言实现,独立存在于虚拟机外部,并且全都继承自抽象类java.lang.ClassLoader。
站在Java 开发人员的角度来看,类加载器就应当划分得更细致一些。自 JDK 1.2 以来, Java 一直保持着三层类加载器、双亲委派的类加载架构,尽管这套架构在Java 模块化系统出现后有了一些调整变 动,但依然未改变其主体结构,我们将在7.5 节中专门讨论模块化系统下的类加载器。
本文将针对JDK 8 及之前版本的 Java 来介绍什么是三层类加载器,以及什么是双亲委派模型。对于这个时期的Java 应用,绝大多数 Java 程序都会使用到以下 3 个系统提供的类加载器来进行加载。

图中展示的各种类加载器之间的层次关系被称为类加载器的 " 双亲委派模型 ( Parents Delegation Model) " 。双亲委派模型要求除了顶层的启动类加载器外,其余的类加载器都应有自己的父类加载器。不过这里类加载器之间的父子关系一般不是以继承(Inheritance )的关系来实现的,而是通常使用组合(Composition )关系来复用父加载器的代码。

双亲委派模型的工作原理:

在双亲委派模型中,当一个类加载器(ClassLoader)收到类加载的请求时,它不会立即尝试自己加载这个类,而是首先将这个请求委派给它的父类加载器去处理。这个过程会一直递归进行,直到达到最顶层的启动类加载器(Bootstrap ClassLoader)。如果父类加载器能够加载这个类,就返回这个类的Class对象给子类加载器;如果父类加载器无法加载这个类,子类加载器才会尝试自己去加载。

注:双亲委派模型保证了java类不会被篡改,从而增强了Java程序的安全性和稳定性。

2. 破坏双亲委派

前言
上面提到过双亲委派模型并不是一个具有 强制性约束的模型 ,而是 Java 设计者推荐给开发者们的 类加载器实现方式。在Java 的世界中大部分的类加载器都遵循这个模型,但也有例外的情况,直到 Java模块化出现为止,双亲委派模型主要出现过3 次较大规模 " 被破坏 " 的情况。
第一次被破坏:
双亲委派模型的第一次"被破坏"其实发生在双亲委派模型出现之前------即JDK 1.2面世以前的"远古"时代。由于双亲委派模型在JDK 1.2之后才被引入,但是类加载器的概念和抽象类
java.lang.ClassLoader 则在 Java 的第一个版本中就已经存在,面对已经存在的用户自定义类加载器的代码,Java 设计者们引入双亲委派模型时不得不做出一些妥协。
为了兼容这些已有代码,无法再以技术手段避免loadClass()被子类覆盖的可能性,只能在 JDK1.2 之后的 java.lang.ClassLoader 中添加一个新的 protected方法 findClass() ,并引导用户编写的类加载逻辑时尽可能去重写这个方法,而不是在 loadClass()中编写代码。上节我们已经分析过 loadClass() 方法,双亲委派的具体逻辑就实现在这里面, 按照loadClass() 方法的逻辑,如果父类加载失败,会自动调用自己的 findClass() 方法来完成加载,这样既不影响用户按照自己的意愿去加载类,又可以保证新写出来的类加载器是符合双亲委派规则的。
第二次被破坏:
双亲委派模型的第二次"被破坏"是由这个模型自身的缺陷导致的,双亲委派很好地解决了各个类
加载器协作时基础类型的一致性问题(越基础的类由越上层的加载器进行加载),基础类型之所以被称为" 基础 " ,是因为它们总是作为被用户代码继承、调用的 API 存在,但程序设计往往没有绝对不变的完美规则,如果有基础类型又要调用回用户的代码就会产生问题
第三次被破坏:
双亲委派模型的第三次"被破坏"是由于用户对程序动态性的追求而导致的,这里所说的"动态性"指的是一些非常"热"门的名词:代码热替换(Hot Swap)、模块热部署(Hot Deployment)等。说白了就是希望Java应用程序能像我们的电脑外设那样,接上鼠标、U盘,不用重启机器就能立即使用,鼠标有问题或要升级就换个鼠标,不用关机也不用重启。对于个人电脑来说,重启一次其实没有什么大不了的,但对于一些生产系统来说,关机重启一次可能就要被列为生产事故,这种情况下热部署就对软件开发者,尤其是大型系统或企业级软件开发者具有很大的吸引力。
OSGi实现模块化热部署的关键是它自定义的类加载器机制的实现,每一个程序模块( OSGi 中称为Bundle)都有一个自己的类加载器,当需要更换一个 Bundle 时,就把 Bundle 连同类加载器一起换掉以实现代码的热替换。
在OSGi环境下,类加载器不再双亲委派模型推荐的树状结构,而是进一步发展为更 加复杂的网状结构,当收到类加载请求时,OSGi 将按照下面的顺序进行类搜索:
1)将以java.*开头的类,委派给父类加载器加载。
2)否则,将委派列表名单内的类,委派给父类加载器加载。
3)否则,将Import列表中的类,委派给Export这个类的Bundle的类加载器加载。
4)否则,查找当前Bundle的ClassPath,使用自己的类加载器加载。
5)否则,查找类是否在自己的Fragment Bundle中,如果在,则委派给Fragment Bundle的类加载器 加载。
6)否则,查找Dynamic Import列表的Bundle,委派给对应Bundle的类加载器加载。
7)否则,类查找失败。
注意: 上面的查找顺序中只有开头两点仍然符合双亲委派模型的原则,其余的类查找都是在平级的类加 载器中进行的。

相关推荐
找不到、了10 小时前
JVM核心知识整理《1》
jvm
L.EscaRC12 小时前
面向 Spring Boot 的 JVM 深度解析
jvm·spring boot·后端
学到头秃的suhian1 天前
JVM-类加载机制
java·jvm
NEFU AB-IN1 天前
Prompt Gen Desktop 管理和迭代你的 Prompt!
java·jvm·prompt
唐古乌梁海2 天前
【Java】JVM 内存区域划分
java·开发语言·jvm
众俗2 天前
JVM整理
jvm
echoyu.2 天前
java源代码、字节码、jvm、jit、aot的关系
java·开发语言·jvm·八股
代码栈上的思考2 天前
JVM中内存管理的策略
java·jvm
thginWalker2 天前
深入浅出 Java 虚拟机之进阶部分
jvm
沐浴露z2 天前
【JVM】详解 线程与协程
java·jvm