双亲委派机制【JVM:类加载机制深度剖析】 - 第7篇
本文在文章底部有配套学习视频
前言
在前面我们学习了这个Java的几个类加载,我们也自定义了一个我们自己的类加载器,那么这些类加载之间有关系嘛?
一、双亲委派机制
1.1 概念
JVM类加载器是有亲子层级结构的,如下图:
这里类加载其实就有一个双亲委派机制,加载某个类时会先委托父加载器寻找目标类,找不到再委托上层父加载器加载,如果所有父加载器在自己的加载类路径下都找不到目标类,则在自己的类加载路径中查找并载入目标类。
比如我们的Math类,最先会找应用程序类加载器加载,应用程序类加载器会先委托扩展类加载器加载,扩展类加载器再委托启动类加载器,顶层启动类加载器在自己的类加载路径里找了半天没找到Math类,则向下退回加载Math类的请求,扩展类加载器收到回复就自己加载,在自己的类加载路径里找了半天也没找到Math类,又向下退回Math类的加载请求给应用程序类加载器,应用程序类加载器于是在自己的类加载路径里找Math类,结果找到了就自己加载了。
画外音:双亲委派机制说简单点就是,先找父亲加载,不行再由儿子自己加载。
1.2 为什么要设计双亲委派机制?
(1)沙箱安全机制:自己写的java.lang.String.class类不会被加载,这样便可以防止核心API库被随意篡改。
(2)避免类的重复加载:当父亲已经加载了该类时,就没有必要子ClassLoader再加载一次,保证被加载类的唯一性。
我们思考下为什么要从下线上委托,从上往下加载。设想一下,如果我们是从下往上加载的话,会是多么可怕的一件事情:我们不小心定义了一个java.lang.String类和jdk的String一样的,如果是从下往上加载的话,那么我们自己定义的会优先被加载,那么jdk的String就无法被加载了,那么String中的方法也就无法被调用了,这将是多么可怕的一个事情。
1.3 沙箱安全机制小栗子
1.3.1 例子1
我们定义一个java.lang.String类:
package java.lang;
public class String {
public static void main(String[] args) {
System.out.println("**************My String Class**************");
}
}
右键运行就会看到控制台爆红了:
这说明了什么:找不到main方法,也就说明了我们类加载不是加载了我们应用中的String,而是JDK中的String了。
1.3.2 例子2
我们接下来尝试下使用我们自己的类加载MyClassLoaderTest加载下:
(1)首先将上面的String.class类放到/data/java/java/lang下;
(2)修改代码:classLoader.loadClass("java.lang.String");
// 尝试去加载 java.lang.String
Class clazz = classLoader.loadClass("java.lang.String");
// 不能调用clazz.getClassLoader(): 因为是由启动类加载器加载的,启动类加载器为native code,返回的值为null.
System.out.println(clazz);
System.out.println(clazz.getClassLoader());
此时运行运行下看下效果:
这说明了什么:找到了启动类加载器,启动加载器为native code。
相关历史文章(阅读本文之前,您可能需要先看下之前的系列👇)
JVM内存模型和调优实战课程
http://t.cn/A6wWMVqG
点击「阅读原文」快速查看学习:
学习的天平上,你在左盘付出的越多,那么在右盘得到的也会随着你的付出而增多。致敬大师,致敬未来的你。