JDK9引入了模块化系统来实现可配置的封装隔离机制,同时JVM对类加载的架构也做出了调整,也就是双亲委派模型的第四次破坏

01 双亲委派模型

简介

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

  • 启动类加载器(Bootstrap Class Loader):由C++编写,负责加载<JAVA_HOME>\jre\lib目录下的类,例如最基本的Object,Integer,这些存在于rt.jar文件中的类,一般这些类都是Java程序的基石。

  • 扩展类加载器(Extension Class Loader):负责加载<JAVA_HOME>\jre\lib\ext目录下的类,在JDK9之前我们可以将通用性的类库放在ext目录来扩展JAVA的功能,但实际的工程都是通过maven引入jar包依赖。并且在JDK9取消了这一类加载器,取而代之的是平台类加载器(Platform Class Loader),下面会对其介绍。

  • 应用类加载器(Application Class Loader):负责加载ClassPath路径下的类,通常工程师编写的大部分类都是由这个类加载器加载。

工作顺序

解释

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

源代码

下面代码保留了核心逻辑,并添加了注释,主要是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);
...}}

图示

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

缺点

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

  • 启动类加载器负责加载<JAVA_HOME>\jre\lib目录

  • 扩展类加载器负责加载<JAVA_HOME>\jre\lib\ext目录

  • 应用类加载器负责加载ClassPath目录。

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

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

02 JDK的模块化

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

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

03 模块化加载源码

 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<String, LoadedModule> packageToModule= new ConcurrentHashMap<>(1024);

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

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

在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 {        //BootClassLoader        BOOT_LOADER =            new BootClassLoader((append != null && !append.isEmpty())                ? new URLClassPath(append, true)                : null);        //PlatformClassLoader        PLATFORM_LOADER = new PlatformClassLoader(BOOT_LOADER);        ...        //AppClassLoader        APP_LOADER = new AppClassLoader(PLATFORM_LOADER, ucp);    }  }

04 结论

  1. 经过破坏后的双亲委派模型更加高效,减少了很多类加载器之间不必要的委派操作

  2. JDK9的模块化可以减少Java程序打包的体积,同时拥有更好的隔离线与封装性

    每个module拥有专属的类加载器,程序在并发性上也会更加出色

有道无术,术可成;有术无道,止于术

欢迎大家关注Java之道公众号

好文章,我在看❤️

谈谈双亲委派模型的第四次破坏-模块化相关推荐

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

    前言 JDK9引入了Java模块化系统(Java Platform Moudle System)来实现可配置的封装隔离机制,同时JVM对类加载的架构也做出了调整,也就是双亲委派模型的第四次破坏.前三次 ...

  2. JVM 类加载器与双亲委派模型

    1. 类加载器 我们知道,虚拟机在加载类的过程中需要使用类加载器进行加载,而在 Java 中,类加载器有很多,那么当 JVM 想要加载一个 .class 文件的时候,到底应该由哪个类加载器加载呢?这时 ...

  3. 双亲委派模型和破坏性双亲委派模型详解

    从JVM的角度来看,只存在两种类加载器: 启动类加载器(Bootstrap ClassLoader):由C++语言实现(针对HotSpot),负责将存放在<JAVA_HOME>\lib目录 ...

  4. 什么情况下需要破坏双亲委派模型

    双亲委派模型的破坏 双亲委派模型的第一次"被破坏"其实发生在双亲委派模型出现之前–即JDK1.2发布之前.由于双亲委派模型是在JDK1.2之后才被引入的,而类加载器和抽象类java ...

  5. Java双亲委派模型是什么、优势在哪、双亲委派模型的破坏

    定义 双亲委派模式的工作原理的是;如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行,如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终 ...

  6. java类加载和双亲委派模型浅说

    本文目录 前言 一.类加载器 1.1 类加载机制的基本特征 1.2 类加载的分类 1.3 类加载器 A.启动类加载器(引导类加载器,Bootstrap ClassLoader) B.扩展类加载器(Ex ...

  7. Java 双亲委派模型机制

    前言 Java是运行在Java的虚拟机(JVM)中的,在初步学习Java时,我们都知道,编写的Java源代码会被编译器编译成.class的字节码文件.然后ClassLoader负责将这些class文件 ...

  8. JVM 破坏双亲委派模型

    双亲委派模型并不是一个具有强制性约束的模型,而是Java设计者推荐给开发者们的类加载器实现方式.在 Java的世界中大部分的类加载器都遵循这个模型,但也有例外的情况,直到Java模块化出现为止,双亲委 ...

  9. 深入理解 Tomcat(四)Tomcat 类加载器之为何违背双亲委派模型

    这是我们研究Tomcat的第四篇文章,前三篇文章我们搭建了源码框架,了解了tomcat的大致的设计架构, 还写了一个简单的服务器.按照我们最初订的计划,今天,我们要开始研究tomcat的几个主要组件( ...

最新文章

  1. session和cache的区别是什么?
  2. 【技术好文转载】未来技术前瞻
  3. 程序集信息设置.net
  4. 5G怎样实现波束赋形?
  5. Python使用MongoDB简记
  6. Python之 多重循环
  7. [图文]Chrome四步下载斗鱼视频(直播回放)视频
  8. hutool依赖:BeanUtil工具类的使用:对象转对象、对象转map、map转对象
  9. Handler native层实现原理
  10. 云计算是什么,阿里云提供哪些云服务
  11. 如何查找qq邮箱的服务器地址
  12. 电子书Epub文件剖析
  13. 牛逼的人很早就开始牛逼了
  14. centos7安装gparted分区工具及简单操作
  15. Docker Cgroup资源配置(CPU、内存、磁盘)
  16. 光缆常用的设备测试方法介绍
  17. TigerGraph图数据库创建一个图Schema
  18. jsp页面动态显示本地时间
  19. 投资体系-02-房产投资三级火箭模式与产业思维
  20. 蓝牙跳频算法分析【经典蓝牙 vs BLE 4.x vs BT 5.0 BLE部分】

热门文章

  1. linux中命令对c文件进行编译,Linux下C语言编译基础及makefile的编写
  2. 在c语言中文件的指针是什么,C语言中文件描述符和文件指针的本质区别
  3. adb remount overlayfs的说明
  4. java string类型详解_Java字符串类型详解
  5. 有关 给Layout设置监听事件后,与Layout子控件的响应关系
  6. (操作系统题目题型总结)第五章:设备管理
  7. Cachegrind:缓存和分支预测分析器
  8. C++调用WMI类查询获取操作系统名(实例)
  9. Python pip参数(精)
  10. Python 配置文件之ConfigParser模块(实例、封装)