浅谈一下Java的双亲委派模式

2023-05-18 05:05:17 委派 浅谈 双亲

说起双亲委派模型,不得不说一下类加载器。

类加载器是什么?

当我们编译Java类时,JVM会创建与平台和机器无关的字节码。字节码存储在.class文件中。当我们尝试使用一个类时,类加载器就会把它加载到内存中,然后把字节码文件转成Class对象。通俗的说类加载器就是将.class文件转成Class对象的。

类加载器分类

启动类加载器(Bootstrap Class Loader):负责加载存放在 <JAVA_HOME>\lib目录下的类,或者被-Xbootclasspath参数所指定的路径中存放的类。比如:rt.jarjava.lang.*包下的类。

扩展类加载器(Extension Class Loader):负责加载<JAVA_HOME>\lib\ext目录中,或者被java.ext.dirs系统变量所指定的路径中所有的类库。

应用程序类加载器(Application Class Loader):负责加载用户类路径上所有的类库。

双亲委派模型工作流程

当类加载器收到加载类的请求时,它首先会把请求委派给父加载器去完成,每一层都如此,直到把请求委派给最顶层的启动类加载器,只有当父加载器无法加载委派过来的类时,子加载器才会加载。

JVM在加载⼀个类时,会调⽤AppClassLoader的loadClass⽅法来加载这个类,不过在这个⽅法中,会先使⽤ExtClassLoader的loadClass⽅法来加载类,同样ExtClassLoader的loadClass⽅法中会先使⽤BootstrapClassLoader来加载类,如果BootstrapClassLoader加载到了就直接成功,如果 BootstrapClassLoader没有加载到,那么ExtClassLoader就会⾃⼰尝试加载该类,如果没有加载到,那么则会由AppClassLoader来加载这个类。

双亲委派模型

所以,双亲委派指得是,JVM在加载类时,会委派给ExtClassLoader和BootstrapClassLoader进⾏加载,如果没加载到才由⾃⼰进⾏加载。

这里说的双亲并不是说类加载器之间是以继承方式实现的,而是以组合的方式实现的,通过源码可以证实这点:

java.lang.ClassLoader#loadClass

private final ClassLoader parent;

protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            Class<?> 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 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;
        }
    }

同时,通过源码我们也可以看到类加载器的加载流程是跟我们描述的一样的:先检查请求加载的类型是否已经被加载过,若没有则调用父加载器的loadClass()方法,若父加载器为空则默认使用启动类加载器作为父加载器。假如父类加载器加载失败,抛出ClassNotFoundException异常的话,才调用自己的findClass()方法尝试进行加载。

双亲委派模型好处

保证唯一性,避免重复加载:类的加载随着它的类加载器一起具备了层级关系,通过这种层级关系避免了重复加载,父类加载器加载了该类,子加载器就无需加载了。

避免核心类被篡改:核心类由启动类加载器加载,即使用户自定义同名核心类也不会被加载。

到此这篇关于浅谈一下Java的双亲委派模式的文章就介绍到这了,更多相关java双亲委派模式内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!

相关文章