类加载器 Classloader & 双亲委派模型

Classloader

在了解了类的生命周期与加载过程之后,接下来就需要了解类加载器,即Classloader。

JVM 中内置了三个重要的 ClassLoader,除了 BootstrapClassLoader 其他类加载器全部继承自java.lang.ClassLoader

  • BootstrapClassLoader(启动类加载器)
    最顶层的加载类,由 C++实现,负责加载 %JAVA_HOME%/lib目录下的 jar 包和类或者被 -Xbootclasspath参数指定的路径中的所有类
  • ExtensionClassLoader(扩展类加载器)
    主要负责加载 %JRE_HOME%/lib/ext 目录下的 jar 包和类,或被 java.ext.dirs 系统变量所指定的路径下的 jar 包。
  • AppClassLoader(应用程序类加载器)
    面向我们用户的加载器,负责加载当前应用 classpath 下的所有 jar 包和类。

双亲委派模型

当一个类加载器收到类加载任务时,会先交给自己的父加载器去完成,因此最终加载任务都会传递到最顶层的BootstrapClassLoader,只有当父加载器无法完成加载任务时,才会尝试自己来加载。但是熟悉Java的人都知道,Java中只有父类而没有母类的说法,顾双亲其实具有误导性,本人更愿意理解为,父类委派模型,以下为官方说明

  • 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.

Classloader 加载过程

protected Class loadClass(String name, boolean resolve)
    throws ClassNotFoundException
{
    synchronized (getClassLoadingLock(name)) {
        // 首先检查这个类是否已经被加载,在JVM生命周期中,一个类只会加载一次
        Class c = findLoadedClass(name);
        if (c == null) {
            long t0 = System.nanoTime();
            try {
                if (parent != null) {
                    // 如果父类加载器不为空,则使用父类加载器加载
                    c = parent.loadClass(name, false);
                } else {
                    // 如果父类加载器为空,则使用顶层bootstrap加载器
                    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;
    }
}

双亲委派模型的好处

  • 保证了 Java 程序的稳定运行,可以避免类的重复加载,VM通过类名与加载器确定唯一的类
  • 保证了 Java 核心 API 不被篡改,如果没有使用双亲委派模型,而是每个类加载器加载各自的类,就会出现一些问题,比如开发者自己定义一个名为 java.lang.Object 类,程序运行时,系统就会出现多个不同的 Object 类。

如果不想用双亲委派模型

  • 如果不想打破双亲委派模型,就重写 ClassLoader 类中的 findClass() 方法即可,无法被父类加载器加载的类最终会通过这个方法被加载
  • 如果想打破双亲委派模型则需要重写 loadClass() 方法

自定义类加载器

自定义加载器,需要继承 ClassLoader

你可能感兴趣的:(java)