• 类加载器源码分析-双亲委派机制


    Launcher 源码

    sun.misc.Launcher类是java 虚拟机的入口,在启动 java应用 的时候会首先创建Launcher。在初始化Launcher对象的时候会创建一个ExtClassLoader拓展程序加载器 和 AppClassLoader应用程序类加载器(这俩鬼东西好像只是加载类的路径不一样而已),然后由这俩类加载器去加载应用程序中需要的各种类。

    public class Launcher {
    	// 成员 ClassLoader 类加载器,用来存储 应用程序类加载器,
    	// 此加载器与线程绑定,用作线程的上下文类加载器。
    	private ClassLoader loader;  
    	
    	// 两个静态内部类
    	static class AppClassLoader extends URLClassLoader {...}
    	static class ExtClassLoader extends URLClassLoader {...}
    
    	    // 构造器
    	    public Launcher() {
            Launcher.ExtClassLoader var1;
            try {
                var1 = Launcher.ExtClassLoader.getExtClassLoader();
            } catch (IOException var10) {
                throw new InternalError("Could not create extension class loader", var10);
            }
    
            try {
                this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
            } catch (IOException var9) {
                throw new InternalError("Could not create application class loader", var9);
            }
    
            Thread.currentThread().setContextClassLoader(this.loader);
            String var2 = System.getProperty("java.security.manager");
            if (var2 != null) {
                SecurityManager var3 = null;
                if (!"".equals(var2) && !"default".equals(var2)) {
                    try {
                        var3 = (SecurityManager)this.loader.loadClass(var2).newInstance();
                    } catch (IllegalAccessException var5) {
                    } catch (InstantiationException var6) {
                    } catch (ClassNotFoundException var7) {
                    } catch (ClassCastException var8) {
                    }
                } else {
                    var3 = new SecurityManager();
                }
    
                if (var3 == null) {
                    throw new InternalError("Could not create SecurityManager: " + var2);
                }
    
                System.setSecurityManager(var3);
            }
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    AppClassLoader 源码

    AppClassLoader 就是应用程序类加载器,负责加载我们程序中classpath下的类也就是target目录中classes目录下的字节码文件以及pom文件中引用的第三方jar包。
    AppClassLoader 继承 URLClassLoader 继承 SecureClassLoader 继承 ClassLoader

     static class AppClassLoader extends URLClassLoader { 
            public Class<?> loadClass(String var1, boolean var2) throws ClassNotFoundException {
                // ...
                // 调用了一个 native 本地方法 knownToNotExist(),去查找该类的加载记录
                if (this.ucp.knownToNotExist(var1)) {        // 如果有该类加载记录
                    Class var5 = this.findLoadedClass(var1); // 直接去已经加载的类中找
                    if (var5 != null) {
                        if (var2) {
                            this.resolveClass(var5);
                        }
    
                        return var5;
                    } else {                                 // 如果没找到报异常
                        throw new ClassNotFoundException(var1);
                    }
                } else {                                     // 如果没有该类的加载记录
                    return super.loadClass(var1, var2);      // 调用父类 ClassLoader 的 loadClass()
                }
            }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    ExtClassLoader 源码

    ExtClassLoader 并没有对 loadClass() 方法进行重写,也就是说它直接调用其父类 ClassLoaderloadClass() 方法。

    static class ExtClassLoader extends URLClassLoader {
    	//...
    }
    
    • 1
    • 2
    • 3

    ClassLoader 源码

     /** 允许直接调用的是这个根据 类名加载类 的方法, */
     public Class<?> loadClass(String name) throws ClassNotFoundException {
            return loadClass(name, false); // 内部调用的是类加载方法默认不对类进行解析
        }
    
    • 1
    • 2
    • 3
    • 4
     	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;
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    这个寻找类的方法一般需要实现类重写,否则默认直接抛出 ClassNotFoundException

        protected Class<?> findClass(String name) throws ClassNotFoundException {
            throw new ClassNotFoundException(name);
        }
    
    • 1
    • 2
    • 3

    ClassLoader 维护的线程安全的映射集合,用来存储 类加载器对象它的锁

        // Maps class name to the corresponding lock object when the current
        // class loader is parallel capable.
        // Note: VM also uses this field to decide if the current class loader
        // is parallel capable and the appropriate lock object for class loading.
        private final ConcurrentHashMap<String, Object> parallelLockMap;
    
    • 1
    • 2
    • 3
    • 4
    • 5

    根据类名获取对应的锁(其实就是对应的类加载对象本身

        protected Object getClassLoadingLock(String className) {
            Object lock = this; // 这个锁就是类加载器对象本身!
            if (parallelLockMap != null) {
                Object newLock = new Object(); 
                lock = parallelLockMap.putIfAbsent(className, newLock); // 放入集合里存储
                if (lock == null) { // 如果没有对应的类加载器对象
                    lock = newLock; // 创建一个新的类加载器
                }
            }
            return lock; // 返回类加载器对象
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    findBootstrapClass()是底层系统的本地方法

    	 /**
         * Returns a class loaded by the bootstrap class loader;
         * or return null if not found.
         */
        private Class<?> findBootstrapClassOrNull(String name)
        {
            if (!checkName(name)) return null;
    
            return findBootstrapClass(name);
        }
    
        // 返回 BootstrapClassLoader 启动类加载器加载的类。
        private native Class<?> findBootstrapClass(String name);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    总结

    首先 双亲委派 个人认为应该是个翻译错误。这个翻译非常容易误导人啊!我一拳就过去!

    双亲在汉语里是指父母的意思,双亲委派 直接理解就是 一个类加载器对象去加载类 的时候是先不会自己加载,而是先 委派 它的 父类母类 去加载。而且这里的 委派 改成 委托 更为合理。

    但是根据源码里的逻辑,并不是这样的(坑人):

    1. 一个 类加载器 要加载类(无论是 AppClassLoaderExtClassLoader还是自定义的类加载器),因为他们统统都是 ClassLoader 的子类对象(继承者),最终都是要先委托最底层的 ClassLoader (老太公) 去加载,在这个层层委托的过程中,每一层的类加载器都会判断这个类在不在自己的加载范畴内,如果在则检查之前有没有加载记录,如果有则返回这个类,没有再去加载。但如果这个类不在自己的加载范畴内,则这个类交由它的继承者们自行加载。
    2. ClassLoader是JVM里面的最底层的类加载器,然而我们要加载的资源(类文件)并不是存储在JVM上的,而是存储在宿主机(服务器上)的,所以当ClassLoader要加载类的时候,是通过委托宿主机系统中的类加载器帮它加载的,这个类加载器就是 BootStrapClassLoader (启动类加载器)。
    3. BootStrapClassLoader

    在这里插入图片描述

    ok

    最终总结一下:

    1.所谓的双亲指的就是 ClassLoaderBootStrapClassLoader
    2.ClassLoader 是 java 生态里最底层的类加载器
    3.BootStrapClassLoader 是 C++ 生态中的类加载器。因为 java虚拟机本身就是基于底层系统运行的,它需要依托于底层生态,所以它需要通过底层系统的类加载器去加载资源(类)。而这个加载器就是 BootStrapClassLoader
    4.这个机制的好处就是不会造成类的重复加载。
    5.改名!什么 双亲委派机制,是 溯源委托加载机制树根结点加载机制

    散会!

    请添加图片描述

  • 相关阅读:
    初识OpenGL (-)坐标空间
    有没有什么大学生搜题比较好用的网站或APP
    游戏行业安全选择游戏盾,效果怎么样?
    Shiro实现多realm方案
    Python快速刷题网站——牛客网 数据分析篇(八)
    统计无向图中无法互相到达点对数
    Git 常用命令
    艾美捷QuickTiter 逆转录病毒定量试剂盒测定原理
    集群规模:3 FE + 89 BE
    19、zynq核引出外部引脚
  • 原文地址:https://blog.csdn.net/qq_31856061/article/details/126851941