模块化加载_谈谈双亲委派模型的第四次破坏-模块化

前言

JDK9引入了Java模块化系统(Java Platform Moudle System)来实现可配置的封装隔离机制,同时JVM对类加载的架构也做出了调整,也就是双亲委派模型的第四次破坏。前三次破坏分别是:双亲委派模型推出之前,SPI机制,以及OSGI为代表的热替换机制,这里不细说。

双亲委派模型

简介

在JDK9引入之前,绝大多数Java程序会用下面三个类加载器进行加载

  • 启动类加载器(Bootstrap Class Loader):由C++编写,负责加载jrelib目录下的类,例如最基本的Object,Integer,这些存在于rt.jar文件中的类,一般这些类都是Java程序的基石。
  • 扩展类加载器(Extension Class Loader):负责加载jrelibext目录下的类,在JDK9之前我们可以将通用性的类库放在ext目录来扩展JAVA的功能,但实际的工程都是通过maven引入jar包依赖。并且在JDK9取消了这一类加载器,取而代之的是平台类加载器(Platform Class Loader),下面会对其介绍。
  • 应用类加载器(Application Class Loader):负责加载ClassPath路径下的类,通常工程师编写的大部分类都是由这个类加载器加载。

工作顺序

解释

如果一个ClassLoader收到了类加载的请求,他会先首先将请求委派给父类加载器完成,只有父类加载器加载不了,子加载器才会完成加载。

3697ee3fa1039c2db9e19216912ab793.png

源代码

下面代码保留了核心逻辑,并添加了注释,主要是2个步骤

  1. 如果父类加载器不为空则用父类加载器加载
  2. 父类加载器加载不成功则本身再加载
            Class> c = findLoadedClass(name);            //如果该类没加载过            if (c == null) {                try {                //如果有父类加载器                    if (parent != null) {                    //使用父类加载器加载                        c = parent.loadClass(name, false);                        ...                    }                 }                    if (c == null) {                    ...                    //父类加载器没有加载成功则调用自身的findClass进行加载                    c = findClass(name);...                }            }       

值得注意的是这里的parent并不是继承上的父子关系,而是组合关系的父子,parent只是类加载器的一个参数。

图示

如果觉得上面的解释比较抽象可以看看下面比较形象的图示,这里的敌人就是我们要加载的jar包

8f00938f1868ed57f1ac03ac2fd51de8.png

缺点

通过上面的漫画不言而喻,当真正的敌人来了,靠这种低效的传达机制,怎么可能打一场胜仗呢?

  • 启动类加载器负责加载jrelib目录
  • 扩展类加载器负责加载jrelibext目录
  • 应用类加载器负责加载ClassPath目录。

既然一切都是各司其职,为什么不能加载类的时候一步到位呢?

通过分析JDK9的类加载器源码,我发现最新的类加载器结构在一定程度上是缓解了这种情况的

JDK的模块化

在JDK9之前,JVM的基础类以前都是在rt.jar这个包里,这个包也是JRE运行的基石。这不仅是违反了单一职责原则,同样程序在编译的时候会将很多无用的类也一并打包,造成臃肿。

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

b64c73d28a8381543b959cfc51d8191f.png

模块化加载源码

  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);         }       }

上面代码就是破坏双亲委派模型的“铁证”,而当我们继续跟进findLoadedModule,会发现是根据路径名找到对应的模块,而维护这一数据结构的就是下面这个Map。

Map packageToModule        = new ConcurrentHashMap<>(1024);

可以看到LoadedModule里面不仅有该模块的loader信息,还有用于描述依赖模块,对外暴露模块的信息的mref,LoadedModule也是模块化实现封装隔离机制的一块重要实现。

9682a3972d50f7f85b018136c682461a.png

每一个module信息都有一个BuiltinClassloader,这个类有三个子类,我们通过源码分析他们的父子关系

cc8cd156ed73168bf8da8936b0688930.png

在ClassLoaders类中可以发现,PlatformClassLoader的parent是BootClassLoader,而AppClassLoader的parent则是PlatformClassLoader。

public class ClassLoaders {    // the built-in class loaders    private static final BootClassLoader BOOT_LOADER;    private static final PlatformClassLoader PLATFORM_LOADER;    private static final AppClassLoader APP_LOADER;    static {        BOOT_LOADER =            new BootClassLoader((append != null && !append.isEmpty())                ? new URLClassPath(append, true)                : null);                        PLATFORM_LOADER = new PlatformClassLoader(BOOT_LOADER);        ...        APP_LOADER = new AppClassLoader(PLATFORM_LOADER, ucp);    }  }

结论

  1. 经过破坏后的双亲委派模型更加高效,减少了很多类加载器之间不必要的委派操作
  2. JDK9的模块化可以减少Java程序打包的体积,同时拥有更好的隔离线与封装性
  3. 每个moudle拥有专属的类加载器,程序在并发性上也会更加出色

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/468491.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

第4章 原子操作

有一件事情&#xff0c;你不得不承认&#xff0c;C语言相对汇编来说是高级语言&#xff0c;为什么&#xff0c;因为高级语言会形成封装&#xff0c;比如&#xff0c;我需要把一个变量A&#xff0c;对于CPU来说&#xff0c;先从内存里把这个变量读进运算寄存器&#xff0c;然后运…

龙芯下中标系统C语言查找设备号_龙芯:主控进驻国产激光打印机并适配麒麟OS...

导读&#xff1a;龙芯中科宣布&#xff0c;龙芯1C0300B作为主控芯片&#xff0c;已经批量用于天津光电出品的多款激光打印机中&#xff0c;在打印扫描、通信控制、协议解析方面发挥着重要的作用。图&#xff1a;龙芯1C0300B 主控芯片龙芯1C系列是基于GS232处理器核的高性价比单…

随笔日记

最近加班比较多&#xff0c;昨晚上又坐了最后一班地铁回家&#xff0c;回到家已经是一点了&#xff0c;在路上遇到一个美团外卖的小哥&#xff0c;小哥跟我说&#xff0c;嗨&#xff0c;帅哥&#xff0c;我想借你的头盔用一下&#xff0c;你们可以想象一下&#xff0c;在晚上12…

C#的命名空间

对于大型组织而言&#xff0c;如果涉及到产品线&#xff0c;项目&#xff0c;公共平台很多&#xff0c;如何通过命名空间将所有代码有效的组织起来。就一个目的&#xff0c;用的时候能够很容易的找到。 对于命名空间在大型项目中&#xff0c;必须组织好&#xff0c;一般命名空间…

今日头条关键词排名怎么搜索_公众号搜索关键词排名、公众号怎么排名靠前

真正的互联网老鸟其实都知道&#xff0c;不管在线上推广什么产品都好&#xff0c;其实都没有所谓的难度&#xff0c;获取流量方式其实就是简单的一批&#xff0c;哪里有怎么玄乎&#xff0c;不管以后遇到什么问题&#xff0c;除了首先需要知道的核心关键词&#xff0c;无非就是…

​CPU单挑到群架发展史

CPU 又称中央处理器&#xff0c;搞软件开发的兄弟已经耳熟能详了&#xff0c;CPU 的发展史最基本的矛盾就是软件性能需求的快速提升与 CPU 技术工艺性能提升发展相对缓慢之间的矛盾。摩尔定律也是基于此背景&#xff0c;将硬件性能的有限提升给软件开发者一个预期&#xff0c;告…

JDK、JRE、JVM三者间的关系

JDK&#xff08;Java Development Kit&#xff09;是针对Java开发员的产品&#xff0c;是整个Java的核心&#xff0c;包括了Java运行环境JRE、Java工具和Java基础类库。Java Runtime Environment&#xff08;JRE&#xff09;是运行JAVA程序所必须的环境的集合&#xff0c;包含J…

典型方法_裴礼文老师编数学分析中的典型问题与方法练习参考答案的说明

裴礼文老师编《数学分析中的典型问题与方法》练习参考答案该书共计7章36节1036页&#xff0c;各章由知识点总结、例题和练习组成&#xff0c;知识点基本按照传统《数学分析》教材先后顺序跟进。例题和练习出自《数学分析》经典习题和近年《数学分析》考研试题&#xff0c;极具代…

第4章 原子操作 第二节

-------------------------------------------------------------上一章节说的原子操作&#xff0c;有同学在下面留言说&#xff0c;原子操作不是万能的吧&#xff1f;确实是&#xff0c;原子操作不是万能的&#xff0c;体系结构在完成原子操作部分也是花费了很大的心思&#x…

表格字体缩小 php_如何快速找出两个Excel表格不同的地方?

上一篇文章介绍了如何快速找出两个word文档不同的地方&#xff0c;这篇文章来说说如何快速找出两个Excel表格不同的地方&#xff0c;这里说的两个Excel表格&#xff0c;可以是同一个工作簿的不同sheet&#xff0c;也可以是两个不同的Excel文件&#xff0c;如下图&#xff1a;黄…

周一地铁遐想

连续两周的早上8点出门到晚上12点到家&#xff0c;今天中午睡不着&#xff0c;下午调试代码的时候感觉脑子眼睛有点迷糊&#xff0c;旁晚吃饭的时候&#xff0c;有几个同学还在微信私聊我问题&#xff0c;因为都是加了我的知识星球的同学&#xff0c;咨询的是个人的规划还有以后…

再说鸿蒙

上一篇文章已经被骂狗血了&#xff0c;我一直想找个时间好好澄清一些东西&#xff0c;正好今天晚上睡不着&#xff0c;现在是凌晨4点&#xff0c;我觉得应该再发下东西&#xff0c;当然了&#xff0c;还是以我特有的尿性&#xff0c;没有摆事实讲道理&#xff0c;就是我以为。1…

shell--基本语法

继续运算符模块。 part1 算术运算符 - * / % part2 关系操作 与(&#xff08;&#xff09;)连用 < > < > ! && || 注意&#xff1a;一个等号是赋值操作&#xff0c;两个等号是判断 test命令相关&#xff0c;[]可以达到一样的效果 part3 赋值运算符 * /…

锻炼身体

这周还没有发技术文章&#xff0c;着实是因为忙晕了头&#xff0c;不过不管有多忙&#xff0c;该吃饭的时候还是吃饭&#xff0c;该打球的时候还是打球&#xff0c;打球不是为了赚钱&#xff0c;但是是为了更好的赚钱&#xff0c;很多时候忽略了一些小事情&#xff0c;往往这些…

你今天忘本了吗?

我从小在农村长大&#xff0c;我们村前面有一条很长的河&#xff0c;我家在河的后面的那个小山坡上&#xff0c;背靠着大山&#xff0c;很多大师来我们家&#xff0c;都说我家这里是风水宝地&#xff0c;幸运的是&#xff0c;我就在这块风水宝地里长大了。后来&#xff0c;我很…

评分组件开发

我们知道&#xff0c;许多外卖app都有评分的星星&#xff0c;这里我总结一下对评分组件的开发&#xff0c;学习视频&#xff1a;饿了么实战&#xff08;慕课网&#xff09; 1.html部分 <div class"star" :class"starType"><span v-for"itemC…

第4章 第三节 内核同步

抢占式内核和非抢占式内核Linux 内核有两个空间&#xff0c;一个是内核空间一个是用户空间&#xff0c;如果一个进程正在内核态执行的时候&#xff0c;允许内核打断他的执行&#xff0c;让另一个进程执行&#xff0c;那么这个内核就是可抢占式内核。还有一种情况就是&#xff0…

第四章 第四节 per_cpu

我们上一章说了实现内核同步的方法很多&#xff0c;如下表技术说明适用范围每CPU变量在CPU之间复制数据结构所有CPU原子操作对一个计数器原子地“读-修改-写”的指令所有CPU内存屏障避免指令重新排序本地CPU或所有CPU自旋锁加锁时忙等所有CPU信号量加锁时阻塞等待所有CPU顺序锁…

python的三维图片_python如何做三维图

Python三维绘图在遇到三维数据时&#xff0c;三维图像能给我们对数据带来更加深入地理解。python的matplotlib库就包含了丰富的三维绘图工具。1、创建三维坐标轴对象Axes3D创建Axes3D主要有两种方式&#xff0c;一种是利用关键字projection3dl来实现&#xff0c;另一种则是通过…

为何要使用docker

可能很多人听说过docker&#xff0c;也可能有很多人用过&#xff0c;但是其中的一些细节&#xff0c;可能不是很清楚&#xff0c;还有一些人&#xff0c;像我一样&#xff0c;并不知道docker&#xff0c;也没有用过&#xff0c;刚好最近一个大神朋友比较有空&#xff0c;让他写…