【JVM】jvm的双亲委派机制
创始人
2024-02-06 08:14:27
0

双亲委派机制

  • 一、JVM体系结构
  • 二、双亲委派机制的含义
  • 三、双亲委派机制的源代码
  • 四、双亲委派机制的意义
  • 五、示例代码

一、JVM体系结构

在这里插入图片描述我们先在这里放一张 JVM 的体系架构图,方便我们有个总体认知。

在了解JVM的双亲委派机制之前,你不得不需要知道的几个名字:本文我们只讲上图中里的类加载子系统下的三个阶段之一(Loading,加载阶段)有关的内容,即下图中用红色线圈起来的几个名词。
在这里插入图片描述引导类加载器(Bootstrap ClassLoader);
扩展类加载器(Extension ClassLoader);
应用类加载器(Application ClassLoader)。

有关其详细概念请移步至:【JVM】java的jvm类加载器和类加载子系统之JVM类加载器和类加载子系统里的加载阶段,在这里就不多说了。

二、双亲委派机制的含义

当一个类加载器收到了类加载的请求的时候,它不会直接去加载指定的类,而是把这个请求委托给自己的父加载器去加载。只有父加载器无法加载这个类的时候,才会由当前这个加载器来负责类的加载。

Java中提供如下四种类型的加载器,每一种加载器都有指定的加载对象,具体如下:

  • Bootstrap ClassLoader(引导类加载器) :主要负责加载Java核心类库,%JAVA_HOME%/jre/lib/目录下,resources.jar或者sun.boot.class.path路径下的内容等。
  • Extention ClassLoader(扩展类加载器):主要负责从 java.ext.dirs 系统属性所指定的目录中加载类库,或从JDK的安装目录的**/jre/lib/ext/**子目录(扩展目录)下加载的类库。
  • Application ClassLoader(应用程序类加载器) :主要负责加载当前应用的classpath下的所有类。
  • User-Defined ClassLoader(用户自定义类加载器) : 用户自定义的类加载器,可加载指定路径的class文件。

注意:这里存在的加载器之间的层级关系并不是以继承的方式存在的,而是以组合的方式处理的。

这四种类加载器存在如下关系,当进行类加载的时候,虽然用户自定义类不会由 Bootstrap ClassLoader 或是 Extension ClassLoader 加载(由类加载器的加载范围决定),但是代码实现还是会一直委托到 Bootstrap ClassLoader, 上层无法加载,再由下层是否可以加载,如果都无法加载,就会触发 findclass(),抛出 classNotFoundException

三、双亲委派机制的源代码

打开IDEA等代码开发工具,搜索 ClassLoader 并进入类中,找到 loadClass() 方法,源代码如下:

    /*** Loads the class with the specified binary name. * This method searches for classes in the same manner as the loadClass(String, boolean) method.  * It is invoked by the Java virtual machine to resolve class references.  * Invoking this method is equivalent to invoking #loadClass(String, boolean) loadClass(name,false).** @param:  name The binary name of the class* @return:  The resulting Class object* @throws:  ClassNotFoundException If the class was not found*/public Class loadClass(String name) throws ClassNotFoundException {return loadClass(name, false);}/*** Loads the class with the specified binary name.  * The default implementation of this method searches for classes in the following order:** 		1. Invoke #findLoadedClass(String) to check if the class*   has already been loaded. *   	2. Invoke the #loadClass(String) loadClass method*   on the parent class loader.  If the parent is null the class*   loader built-in to the virtual machine is used, instead.  *   	3. Invoke the #findClass(String) method to find the*   class. * * If the class was found using the above steps, and the* resolve flag is true, this method will then invoke the #resolveClass(Class) method on the resulting Class object.** Subclasses of ClassLoader are encouraged to override * #findClass(String), rather than this method. ** Unless overridden, this method synchronizes on the result of* #getClassLoadingLock getClassLoadingLock method* during the entire class loading process.** @param: name The binary name of the class* @param: resolve If true then resolve the class* @return: The resulting Class object* @throws: ClassNotFoundException If the class could not be found*/protected Class loadClass(String name, boolean resolve)throws ClassNotFoundException{synchronized (getClassLoadingLock(name)) {// First, check if the class has already been loadedClass c = findLoadedClass(name);if (c == null) {long t0 = System.nanoTime();try {if (parent != null) {c = parent.loadClass(name, false);} else {c = findBootstrapClassOrNull(name);}} catch (ClassNotFoundException e) {// ClassNotFoundException thrown if class not found// from the non-null parent class loader}if (c == null) {// If still not found, then invoke findClass in order// to find the class.long t1 = System.nanoTime();c = findClass(name);// this is the defining class loader; record the statssun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}}if (resolve) {resolveClass(c);}return c;}}

其实这段代码已经很好的解释了双亲委派机制,为了大家更容易理解,我做了一张图来描述一下上面这段代码的流程:
在这里插入图片描述从上图中我们就更容易理解了,当一个 Hello.class 这样的文件要被加载时。不考虑我们自定义类加载器,首先会在 AppClassLoader 中检查是否加载过,如果有那就无需再加载了。如果没有,那么会拿到父加载器,然后调用父加载器的 loadClass() 方法。父类中同理也会先检查自己是否已经加载过,如果没有再往上。注意这个类似递归的过程,直到到达 Bootstrap ClassLoader 之前,都是在检查是否加载过,并不会选择自己去加载。直到 Bootstrap ClassLoader,已经没有父加载器了,这时候开始考虑自己是否能加载了,如果自己无法加载,会下沉到子加载器去加载,一直到最底层,如果没有任何加载器能加载,就会抛出 ClassNotFoundException。那么有人就有下面这种疑问了?

为什么为有这样的设计?下面我们再来说下这样设计的意义就明白了这样做的好处了。

四、双亲委派机制的意义

这种设计有个好处是:

  • 第一:避免类的重复加载。
  • 第二:保护程序的安全,防止核心API被随意篡改。

如果有人想替换系统级别的类,比如:String.java。篡改它的实现,在这种机制下这些系统的类已经被Bootstrap ClassLoader 加载过了(因为当一个类需要加载的时候,最先去尝试加载的就是 Bootstrap ClassLoader),所以其他类加载器并没有机会再去加载,从一定程度上防止了危险代码的植入。

  1. 如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行;
  2. 如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的启动类加载器;
  3. 如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,这就是双亲委派模式。
  4. 父类加载器一层一层往下分配任务,如果子类加载器能加载,则加载此类,如果将加载任务分配至系统类加载器也无法加载此类,则抛出异常。

五、示例代码

现在有一个例子,在我们自定义的 String 类中(包名是 java.lang包下创建),写一个main方法,执行它,如下:

package java.lang;public class String {static {System.out.println("这是自定义的String类!");}public static void main(String[] args) {String customClass = new String();System.out.println(customClass);}
}

执行后的结果如下:
在这里插入图片描述可以看到,报错在java.lang.String中找不到类方法。因为String类是启动类加载器创建的,不是我们自定义的String类,故没有main方法。

完结!

相关内容

热门资讯

喜欢穿一身黑的男生性格(喜欢穿... 今天百科达人给各位分享喜欢穿一身黑的男生性格的知识,其中也会对喜欢穿一身黑衣服的男人人好相处吗进行解...
发春是什么意思(思春和发春是什... 本篇文章极速百科给大家谈谈发春是什么意思,以及思春和发春是什么意思对应的知识点,希望对各位有所帮助,...
网络用语zl是什么意思(zl是... 今天给各位分享网络用语zl是什么意思的知识,其中也会对zl是啥意思是什么网络用语进行解释,如果能碰巧...
为什么酷狗音乐自己唱的歌不能下... 本篇文章极速百科小编给大家谈谈为什么酷狗音乐自己唱的歌不能下载到本地?,以及为什么酷狗下载的歌曲不是...
华为下载未安装的文件去哪找(华... 今天百科达人给各位分享华为下载未安装的文件去哪找的知识,其中也会对华为下载未安装的文件去哪找到进行解...
家里可以做假山养金鱼吗(假山能... 今天百科达人给各位分享家里可以做假山养金鱼吗的知识,其中也会对假山能放鱼缸里吗进行解释,如果能碰巧解...
四分五裂是什么生肖什么动物(四... 本篇文章极速百科小编给大家谈谈四分五裂是什么生肖什么动物,以及四分五裂打一生肖是什么对应的知识点,希...
怎么往应用助手里添加应用(应用... 今天百科达人给各位分享怎么往应用助手里添加应用的知识,其中也会对应用助手怎么添加微信进行解释,如果能...
一帆风顺二龙腾飞三阳开泰祝福语... 本篇文章极速百科给大家谈谈一帆风顺二龙腾飞三阳开泰祝福语,以及一帆风顺二龙腾飞三阳开泰祝福语结婚对应...
美团联名卡审核成功待激活(美团... 今天百科达人给各位分享美团联名卡审核成功待激活的知识,其中也会对美团联名卡审核未通过进行解释,如果能...