vlambda博客
学习文章列表

双亲委派机制【JVM:类加载机制深度剖析】 - 第7篇

 本文在文章底部有配套学习视频 

前言

       在前面我们学习了这个Java的几个类加载,我们也自定义了一个我们自己的类加载器,那么这些类加载之间有关系嘛?

一、双亲委派机制

1.1 概念

JVM类加载器是有亲子层级结构的,如下图:

双亲委派机制【JVM:类加载机制深度剖析】 - 第7篇


这里类加载其实就有一个双亲委派机制,加载某个类时会先委托父加载器寻找目标类,找不到再委托上层父加载器加载,如果所有父加载器在自己的加载类路径下都找不到目标类,则在自己的类加载路径中查找并载入目标类。

比如我们的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**************"); }}

右键运行就会看到控制台爆红了:

双亲委派机制【JVM:类加载机制深度剖析】 - 第7篇

       这说明了什么:找不到main方法,也就说明了我们类加载不是加载了我们应用中的String,而是JDK中的String了。

1.3.2 例子2

       我们接下来尝试下使用我们自己的类加载MyClassLoaderTest加载下:

(1)首先将上面的String.class类放到/data/java/java/lang下;

(2)修改代码:classLoader.loadClass("java.lang.String");

// 尝试去加载 java.lang.StringClass clazz = classLoader.loadClass("java.lang.String");// 不能调用clazz.getClassLoader(): 因为是由启动类加载器加载的,启动类加载器为native code,返回的值为null.System.out.println(clazz);System.out.println(clazz.getClassLoader());

此时运行运行下看下效果:

双亲委派机制【JVM:类加载机制深度剖析】 - 第7篇

这说明了什么:找到了启动类加载器,启动加载器为native code。


👇






JVM


http://t.cn/A6wWMVqG