• 快速了解 Java 类加载机制 & 双亲委派


    快速了解 Java 类加载机制 & 双亲委派

    更多内容请查看: JVM 类加载机制深度剖析(JDK 1.8)

    从零开始 Java 加载类步骤
    1. 首先运行底层 C++ 代码创建 JVM 虚拟机
    2. JVM 虚拟机运行底层 C++ 代码创建 **引导类加载器 BootstrapClassLoader **该类在 Java 中是没有定义的,底层是 C++ 代码
    3. 由 引导类加载器 加载 sun.misc.Launcher 类,同时此类会加载创建出 拓展类加载器 ExtClassLoader应用类加载器 AppClassLoader 应用类加载器 (这两个都是 Launcher 的内部类)
    4. 比如现在要加载 java.long.String 类,会调用 AppClassLoader 的 loadClass 方法进行加载,(所有的 ClassLoader 都继承自 java.long.ClassLoader 类)

    sun.misc.Launcher.AppClassLoader#loadClass 的方法最终会调用 super.loadClass

    public Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        int i = name.lastIndexOf('.');
        if (i != -1) {
            SecurityManager sm = System.getSecurityManager();
            if (sm != null) {
                sm.checkPackageAccess(name.substring(0, i));
            }
        }
    
        if (ucp.knownToNotExist(name)) {
            // The class of the given name is not found in the parent
            // class loader as well as its local URLClassPath.
            // Check if this class has already been defined dynamically;
            // if so, return the loaded class; otherwise, skip the parent
            // delegation and findClass.
            // 这里先找之前是否已经加载过该类,如果加载过直接返回实例
            Class<?> c = findLoadedClass(name);
            if (c != null) {
                if (resolve) {
                    resolveClass(c);
                }
                return c;
            }
            throw new ClassNotFoundException(name);
        }
    
        return (super.loadClass(name, resolve));
    }
    
    • 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

    这里是 java.lang.ClassLoader#loadClass(java.lang.String, boolean) 源码

    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) {
                      	// 找 parent ,AppClassLoader 的 parent 是 ExtClassLoader ,Ext 的是空 (BootStrapClassLoader 是 C++ 的所以没有)
                        c = parent.loadClass(name, false);
                    } else {
                      	// ExtClassLoader 的逻辑会走到这里(它都没重写 loadClass 方法) 这里会尝试通过 bootstrap class loader 来获取类
                        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();
                  	// 如果 parent 没找到,则尝试自己找 都是 URLClassLoader 的实现
                    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
    • 37
    • 38
    • 39
    • 40
    • 41

    所谓的 “双亲委派模型” 其实也就是在说 java.lang.ClassLoader#loadClass 的逻辑罢了。

    这里调用栈通过调用 c = parent.loadClass(name, false); 实际上会嵌套几层。

    最终的效果就是 “双亲委派模型”:

    1. 一般情况下我们所有类加载都先从 AppClassLoader 开始尝试加载,判断是否已加载过,已加载过直接返回
    2. 没加载过则调用 parent.loadClass(name, false) 委派给 ExtClassLoader 加载
    3. ExtClassLoader 首先通过 findBootstrapClassOrNull(name); 委派给 BootstrapLoader 在 jre lib 目录下进行加载,加载到直接返回
    4. 未加载到 ExtClassLoader 尝试加载 jre lib/ext 目录下的类返回
    5. 最终回到 AppClassLoader ,如果父类加载到则直接返回,未加载到,尝试自己在项目 class path 目录下加载返回

    loadClass 做了什么?

    实际上 java 加载类分为以下几步:

    1. 加载,将 class 文件首先从磁盘载入到内存中 (合理,文件你得读到内存才能处理)
    2. 验证,验证 class 文件字节码的有效性 (加载的这个文件,我得先校验校验你符不符合我 java 的规则吧)
    3. 准备,给静态变量赋予 java 定义的默认值 (这个值是 java 默认的,不是用户给定的默认值)
    4. 解析,解析的过程是静态链接的过程,即将静态变量&方法的符号引用(字面量即表示方法或者变量的符号,这个时候还没有将他们 load 进内存,我们无法仅仅通过符号操作他们)转变为直接引用(对应的内存地址或者文件句柄,通过这个可以直接定位到其内存位置,有直接引用说明已经 load 内存,可以对其进行操作)
    5. 初始化,给静态变量赋予 用户定义的默认值,执行静态代码块。
    总结

    C++ 代码执行 --> 创建 JVM 虚拟机 --> 初始化类加载器 --> 从磁盘把类加载到内存 --> 运行 main 方法

    你需要关心 JVM 虚拟机是如何创建的吗?不!你只关心你自己。

  • 相关阅读:
    Android DatePicker(日期选择器)、TimePicker(时间选择器)、CalendarView(日历视图)- 简单应用
    腐败游之川藏线
    【python学习第11节:numpy】
    回溯-求出数组的所有子序列【学习算法】
    LeetCode刷题(python版)——Topic65.有效数字
    CAS和多线程密切相关的东西!
    Java学习笔记零基础入门2
    《吐血整理》进阶系列教程-拿捏Fiddler抓包教程(16)-Fiddler如何充当第三者,再识AutoResponder标签-上篇
    vue 改变路由(URL)参数不刷新页面
    如何配置node.js环境
  • 原文地址:https://blog.csdn.net/w903328615/article/details/127675856