学习 JVM 之双亲委派模型

一、什么是双亲委派

意思是类加载的时候,系统会首先判断当前类是否被加载过。已经被加载的类会直接返回,否则才会尝试加载。

加载的时候,首先会把该请求委派该父类加载器的 loadClass() 处理,因此所有的请求最终都应该传送到顶层的启动类加载器 BootstrapClassLoader 中。当父类加载器无法处理时,才由自己来处理。当父类加载器为 null 时,会使用启动类加载器 BootstrapClassLoader 作为父类加载器。( ExtClassLoader 的父类加载器为 null)

双亲委派机制:双亲委派机制是 Java 虚拟机加载一个类时为该类确定类加载器的一种机制 (类加载器顺序在下面)

**自底向上检查类是否被加载 **: 加载的大多数都是用户自己写的类,如果每次都要从上往下遍历完 jre/lib 目录,又遍历 jre/lib/ext 目录,最后又不是,以至于浪费大量时间。

自顶向下尝试加载类

双亲只是从官方文档翻译成双亲,实际这里的双亲其实就指的是父类,没有mother。加载器之间的“父子关系”也不是我们平日所说的那种继承关系,然而只是一种优先级的层次关系。

官方 API 文档对这部分的描述如下:

The Java platform uses a delegation model for loading classes. The basic idea is that every class loader has a "parent" class loader. When loading a class, a class loader first "delegates" the search for the class to its parent class loader before attempting to find the class itself.

二、介绍其中的类加载器

三个类加载器 以及 用户自定义的加载器,以下顺序描述:

  • Bootstrap ClassLoader(启动类加载器) :最顶层的加载类,由 C++实现,负责加载 %JAVA_HOME%/lib目录下的 jar 包和类或者或被 -Xbootclasspath参数指定的路径中的所有类。
  • Extension ClassLoader(扩展类加载器) : 主要负责加载目录 %JRE_HOME%/lib/ext 目录下的 jar 包和类,或被 java.ext.dirs 系统变量所指定的路径下的 jar 包。
  • Application ClassLoader(应用程序类加载器) :面向我们用户的加载器,负责加载当前应用 classpath 下的所有 jar 包和类。
  • User Defined ClassLoader (用户自定义加载类):用户自定义加载器继承 ClassLoader ,重写 ClassLoader 类中的 findClass() 方法

何时被创建?

启动类加载器,在调用 Java 程序前就被创建。扩展类加载器和应用程序类加载器都是 Launcher 的静态内部类,所以是创建 启动器实例 sun.misc.Launcher 的时候就会被创建。

三、双亲委派的源码

private final ClassLoader parent;
protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // 首先,检查请求的类是否已经被加载过
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {//父加载器不为空,调用父加载器loadClass()方法处理
                        c = parent.loadClass(name, false);
                    } else {//父加载器为空,使用启动类加载器 BootstrapClassLoader 加载
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                   //抛出异常说明父类加载器无法完成加载请求
                }

                if (c == null) {
                    long t1 = System.nanoTime();
                    //自己尝试加载
                    c = findClass(name);

                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

四、双亲委派的好处

  1. 双亲委派模型保证了 Java 程序的稳定运行,可以避免类的重复加载(JVM 区分不同类的方式不仅仅根据类名,相同的类文件被不同的类加载器加载产生的是两个不同的类)

  2. 防止核心API库被随意篡改,例如哪些 String 等等

五、参考

剖析面试最常见问题之JVM(下)

从源码分析 双亲委派机制

posted @ 2021-04-09 22:37  东郊  阅读(91)  评论(0编辑  收藏  举报