每日一面——什么是双亲委派模型?
菜鸡每日一面系列打卡31天
每天一道面试题目
助力小伙伴轻松拿offer
坚持就是胜利,我们一起努力!
题目描述
什么是双亲委派模型?
题目分析
上篇文章我们总结了类加载机制的相关内容,主要介绍了什么是类加载机制,以及类加载机制的过程。而本篇文章提到的双亲委派模型与类加载过程息息相关。为什么这么说呢?在这里,菜鸡先卖个关子。
相较于之前的运行时数据区等的相关内容,双亲委派模型更贴近应用层面一些,因此,也是面试官乐于考查的内容。接下来,随菜鸡一起去看看吧!
题目解答
开篇先上一张图,上一篇关于类加载机制的讲解都是根据这张图进行展开的。
由上图可以看出,类加载的过程,指的是加载、验证、准备、解析、初始化的过程。
我们重点来看类加载过程中的加载阶段,在该阶段做了三件事:
通过一个类的全限定名来获取定义此类的二进制字节流。
将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。
而我们今天要提到的“双亲委派模型”就与第一件事——“通过一个类的全限定名来获取定义此类的二进制字节流”脱不了干系。为什么这么说呢?在给出答案之前,我们需要先了解一个概念——类加载器。
事实上,“通过一个类的全限定名来获取定义此类的二进制字节流”这个动作,并不是JVM内部实现的,而是放在了JVM外部去实现。实现这个动作的代码,就是上文提到的类加载器。
更进一步讲,从JVM层面讲,类加载器只有两种,一种是启动类加载器(Bootstrap Class Loader),另一种是其他类加载器。启动类加载器在HotSpot中是由C++语言实现的,它是虚拟机的一部分,而其他类加载器是由Java实现的,它们独立于虚拟机而存在,并且全部继承自抽象类java.lang.ClassLoader。
如果对这些类加载器细分,可分为三层(对JDK1.9之前的版本而言)。
启动类加载器(Bootstrap Class Loader),上文已经说过,在此不再赘述;
扩展类加载器(Extension Class Loader),它负责加载<JAVA_HOME>\lib\ext目录中,或者被java.ext.dirs系统变量所指定的路径中的所有类库;
应用程序类加载器(Application Class Loader),它负责加载用户类路径(ClassPath)上的所有类库。另外,用户还可以加入自定义的类加载器进行拓展。
下图可以清晰地描述上述类加载器之间的关系。
而这张图所展示的各种类加载器之间的层次关系,就是双亲委派模型!
第二部分中写道,图中所示的各种类加载器之间的层次关系,就是双亲委派模型。
双亲委派模型的工作过程,用语言可以作如下描述:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器(注意,上图已经指出,这里的父子关系是组合关系而非继承关系)去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到最顶层的启动类加载器中,只有当父类加载器反馈自己无法完成这个加载请求时,子加载器才会尝试自己去完成加载。
但这种模型并不是强约束限制的,也就是说,如果有充分的理由,就可以打破该模型的限制。例如,为了实现模块化热部署,OSGi环境采用了网状结构,其中的大部分内容并不符合双亲委派模型的原则,在此不再展开。
另外,在JDK1.9之后,类加载器的委派关系也发生了较大变化,首先,由于整个JDK都基于模块化进行构建,那么扩展类加载器便没有存在的意义了,取而代之的是平台类加载器(Platform Class Loader)。启动类加载器也由虚拟机内部实现转为虚拟机内部和Java类库(增加了BootClassLoader类)协作实现,启动类加载器、平台类加载器、应用程序类加载器全部继承于BuiltinClassLoader。
变化后的类加载器之间的层次关系如下图所示。
其工作过程也发生了变化:当平台及应用程序类加载器收到类加载请求,在委派给父类加载器之前,首先判断该类是否能够归属到某一个系统模块中,如果能找到这种归属关系,则优先委派给负责那个模块的加载器完成加载。
以上便是菜鸡对双亲委派模型的一些总结,供大家参考。
参考资料
《深入理解Java虚拟机》第3版 周志明
相关链接
学习 | 工作 | 分享
👆长按关注“有理想的菜鸡”