「JVM 类加载子系统」系列文章

双亲委派模型

前言

在正式介绍双亲委派模型之前,要思考两个问题,为什么要研究类加载的过程?为什么要研究双亲委派模型?

研究类加载的过程是为了知道加载过程使用到了双亲委派模型,但仅仅知道使用了双亲委派模型还不够,最终目的是要弄清楚为什么要使用双亲委派模型?!双亲委派模型的原理?!双亲委派模型背后的逻辑思想?!该思想是否可以被我们借鉴,为我所用?!

比如:双亲委派模型避免了类的重复加载,避免了核心类库被修改。那么我们在设计框架时,框架底层的内容要不容易被篡改,或者不被攻击,这个时候就可以借鉴双亲委派模型!!

概念

通过上一部分输出的文件目录可以看出,「应用程序类加载器」加载的文件包含了「启动类加载器」加载的文件和「扩展类加载器」加载的文件,那这是不是意味着重复加载了呢?!

其实不然,根据前文 类加载器 的介绍「应用程序类加载器」主要负责加载用户类路径 (ClassPath) 上所有的类库,也就是对应输出中的../../../../concurrency/target/classes

之所以不会重复加载,完全是因为双亲委派模型,它分为两个过程:

6

注意:双亲委派模型要求除了顶层的启动类加载器外,其余的类加载器都应有自己的父类加载器。不过这里类加载器之间的父子关系一般不是继承来实现的,而是通常使用组合关系来复用父加载器的代码

工作过程:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传到最顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求 (它的搜索范围中没有找到所需的类) 时,子加载器才会尝试自己去完成加载

好处

1. 可以避免类重复加载

2. 可以防止核心 API 库被随意修改

3. 全盘委托机制

源码分析

关于三个类加载器的源码分析可见 剖析 [Bootstrap、Extension、Application] ClassLoader

133

C++ 语言调用了sun.misc.Launcher.getLauncher()获取了 launcher 对象,Launcher 类初始化的时候其构造器创建了 ExtClassLoader 和 AppClassLoader,然后接下来调用 launcher 对象的getClassLoader()方法

调用getClassLoader()方法获得了loader对象,而loader对象在构造函数中被赋值

类加载器通过调用loader.loadClass("com.lfool.My")来加载类,根据上面的分析,可以知道双亲委派模型的起点是AppClassLoader,也正如前文 类加载器 所说:如果应用中没有自定义类加载器,一般情况下使用的就是 AppClassLoader 作为默认类加载器

下图分别是 AppClassLoader 和 ExtClassLoader 的关系图 (IDEA 快捷键:Option + Command + U)

66

向上委托查找,向下加载