共计 3341 个字符,预计需要花费 9 分钟才能阅读完成。
前言
上一节咱们简略理解了 jvm 类加载器的步骤并详细分析了 jvm 类加载步骤的具体细节,本节将会接着讲述对于双亲委派机制的细节。双亲委派机制是 jvm 一个类加载的重要加载机制,它是 jvm 的类继承构造的底层设计也是 jvm 类加载的外围步骤,咱们通常应用的 tomcat 对于双亲委派机制进行了毁坏这也是须要理解的内容。
概述
上面是书中 jvm 虚拟机执行引擎的内容概括:
虚拟机和类加载机制概述
把握双亲委派模型
三层模型
启动加载器
扩大类加载器
毁坏:在 jdk9 中转化为平台加载器
应用程序类加载器
Osgi 模型
什么是类加载器
理解类加载器的作用
对于类加载器的理论利用
java 模块化对双亲委派模型的影响
类加载的六个步骤
加载
连贯
验证
筹备
解析
初始化
应用
卸载
本文内容
- 讲述双亲委派机制的基本原理
- 模块化对于类加载器的影响
思维导图
上面是对应的幕布思维导图地址:https://www.mubucm.com/doc/6v…
类和类加载器
在本系列的第一篇咱们讲述了类加载器是加载类的外围,当咱们写好的 java 文件被 jvm 虚拟机编译为.class 文件并且加载到 jvm 虚拟机当中转为字节码指令,通过执行字节码指令实现咱们编写的程序。一个类要被加载首先须要被 jvm 意识才行,而意识它就是靠的类加载器,类加载器毫无疑问就是来加载类的。
自定义类加载器
书中给了一个自定义类加载器的案例,这里间接贴过来了。这个类加载器所做的事件就是简略的构建一个类加载,然而在最初进行 instanceof 的操作的时候,发现后果是 false,这是因为类加载器加载的类是自定义的,而不是 jvm 程序生成的类。
这里也能够认为如果有自定义加载器,则在进行类加载器判断的时候,须要进行“加载”的操作。因为在这里它存在 两个 加载器。
public class ClassLoaderTest {public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {ClassLoader myLoader = new ClassLoader() { @Override
public Class<?> loadClass(String name) throws ClassNotFoundException { try {String fileName = name.substring(name.lastIndexOf(".") + 1)+".class"; InputStream is = getClass().getResourceAsStream(fileName);
if (is == null) {return super.loadClass(name); }
byte[] b = new byte[is.available()]; is.read(b);
return defineClass(name, b, 0, b.length);
} catch (IOException e) {throw new ClassNotFoundException(name);
} }
};
Object obj = myLoader.loadClass("com.headfirst.classloader.ClassLoaderTest").newInstance();
System.out.println(obj.getClass());
System.out.println(obj instanceof com.headfirst.classloader.ClassLoaderTest);
}/* 运行后果:class com.headfirst.classloader.ClassLoaderTest
false
*/
}
什么是双亲委派机制?
双亲委派机制指的是 jvm 的一品种加载机制,jvm 的类加载构造如下图:
结构图如上所示,将分为三层构造,别离是启动类加载器,扩大类加载器和应用程序加载器,这三个加载器首先通过应用程序加载,如果发现无奈加载类,则向上委托给父类的扩大类加载器进行加载,如果同样加载则持续网上进行申请加载,最初如果顶层启动类也无奈加载,这时候又回从顶层向下进行加载,直到自定义加载器能够加载,最初如果都无奈加载,则抛出相干的异样。
对于启动类加载器等等具体的工作流程因为系列晚期的文章曾经讲述过这里间接给出链接:
## 对于 IBM 的 OSGI
osgi 是当初 ibm 公司为了 jdk 模块化标准做的一个致力,当然当初早曾经被 jdk9 的模块化取代,当然这也能够说是商业竞争下的一种斗争,最初的后果就是 java 的模块化和预期成果存在肯定的差距,并且尽管还是毁坏了双亲委派的机制,然而在整体上仍旧能放弃根本的类加载器的结构设计。
这让我想到了 Jrocket 和 HotSpot 的合并,其实成果远不如官网设计的完满成果,总是要差那么一点。
上面是 osgi 的运行流程,简略理解即可:
- Java.* 结尾,父类加载器加载
- 委派名单列表,父类加载器加载
- import 列表委派给 export bundle 类加载器加载
- 查找 Bundler 的 class path,本人类加载器
- 查找本人的 Fragmet Bundle
- 委派 Bundle 的类加载加载
- 类查找失败
## Java 双亲委派模型的挑战
双亲委派机制受到过三次挑战(也能够说是四次),因为双亲委派机制是 jdk1.2 才退出的,为了向前兼容在过后的为了斗争抉择了一个不太好的实现就是应用 findClass()
的办法进行了重写,然而这个办法在后续的兼容过程中很快就出了问题,如果有根底类型要回调上层的类比方典型的 JNDI 服务就是如此,这时候为了兼容又只能不太好的设计就是新增一个 线程上下文类加载器,这个加载器能够了解为在启动类加载器做了一个插件,如果用户本人实现了这个插件,就会调用客户的代码,否则就会从父类的加载器中进行继承。
这个 线程上下文加载器就是 tomcat 实现毁坏双亲委派机制的外围。当然不止 tomcat,很多框架也有用到这个线程上下文加载器,在类的加载的阶段“做手脚”。
接着对于程序热替换的挑战了,简略来说就是程序的模块化,对于模块化的内容,在《Java8 实战》的书籍外面有简略的实践根底探讨,然而如果要深刻模块化,内容有一本书能够来讲,这里也不再赘述对于 OSGI 和 Jisaw 的历史了,对于他所做的事件能够看上一个大节。
最初就是 Jdk9 实现模块化之后的平台类加载器了,这个类加载器基本上算是毁坏了 JDK 的规定,下文会有具体的介绍,如果简略了解这个类加载器就是把之前始终不太优雅的线程上下文加载器通过底层实现了,也就是说平台类加载器将能够依据模块定义的类加载器进行自定义的加载操作。
最初简略总结下面的内容如下:
1. Jdk1.2 之前旧代码兼容,应用 findClass 防止对 loadClass() 重写
2. JNDI 在 Thread.setContextClassLoader() 进行设置
jdk6 对 serialLoader 取代后果
3. 程序动态性与模块化问题
4. 平台加载器毁坏原有类加载规定,线程上下文类加载器的底层兼容实现
## 模块化之后双亲委派模型变动
撇去模块化对于类加载的其余细节,咱们这里间接讲述模块化之后最大的变动:扩大类加载器(Ext)替换为 平台加载器(plaform),次要变动是平台加载器和应用程序加载器不再派生自 URLClassLoader
,因为存在BootClassLoader
为了兼容旧版的加载器返回 null 应用下层疏导器这一条件,后果不会返回进去。
JDK 9 中尽管依然维持着三层类加载器和双亲委派的架构,但类加载的委派关系也产生了 变动。当平台及应用程序类加载器收到类加载申请,在委派给父加载器加载前,要先判断该类是否能 够归属到某一个零碎模块中,如果能够找到这样的归属关系,就要 优先委派给负责那个模块的加载器 实现加载,兴许这能够算是对双亲委派的第四次毁坏?其实从集体看来更像是扭转设计不太优雅的“线程上下文加载器”。
# 总结
本文的内容也比较简单,同样理解即可。重点在于记住双亲委派机制的步骤以及他的工作原理。
# 写在最初
下篇文章将会针对分派的内容进行解说,也是 jvm 十分重要的一部分,通过分派的学习能够对重载、多态、重写有更多的理解。