• 【JVM】 类加载器 ClassLoader


    今日鸡汤:不学原理或许可以走的很快,但是学了原理会帮我们走的更远 …

    一、前言

    我们点击运行,然后我们的 Java 代码就会被编译器编译成 .class 字节码文件,然后我们的 .class 字节码文件就会被交给 JVM 进行类加载 。下面是 JVM 根据得来的 .class 字节码文件 创建一个对象的对象创建流程如下:

    请添加图片描述

    在一个对象创建流程中,我们的类加载器会判断我们的这个类是否已经被加载,加载好的类具有类的所有数据访问入口,这样才能被分配内存,进行后续的操作,如果说类没被加载,我们JVM就会帮我们去加载这个类,我们的双亲委派机制就发生在这个过程中 。

    二、JVM 类加载器

    JVM 具有 4 种类加载器:

    1. 引导类加载器 : 负责加载支持 JVM 运行的位于 JRE 的 lib 目录下的核心类库,比如 rt.jar 、charsets.jar 等等
    2. 扩展类加载器:负责加载支撑 JVM 运行的位于 JRE 的 lib 目录下的 ext 扩展目录中的 JAR 类包
    3. 应用程序类加载器:负责加载 ClassPath 路径下的类包,就是我们自己写的那些类
    4. 自定义类加载器:负责加载用户自定义路径下的类包

    代码示例:

    import sun.misc.Launcher;
    import java.net.URL;
    
    /**
     * @Author: WanqingLiu
     * @Date: 2022/12/02/15:11
     */
    public class DemoClassLoader {
        public static void main(String[] args) {
        	// String 是最高级的类加载器,也就是我们的引导类加载器加载器
        	// 打印 null ———— Java 中无法打印出引导类加载器地址
            System.out.println(String.class.getClassLoader());
            // 扩展类加载器加载的
            // sun.misc.Launcher$ExtClassLoader@7ea987ac
            System.out.println(com.sun.crypto.provider.DESKeyFactory.class.getClassLoader());
            // 我们自己写的类肯定是应用类加载的
            // sun.misc.Launcher$AppClassLoader@18b4aac2
            System.out.println(TestJDKClassLoader.class.getClassLoader());
            // 类加载器具有父子级关系
            ClassLoader appClassLoader = ClassLoader.getSystemClassLoader();
            ClassLoader extClassLoader = appClassLoader.getParent();
            ClassLoader bootstrapClassLoader = extClassLoader.getParent();
            System.out.println("引导类加载器为:" + bootstrapClassLoader);
            System.out.println("扩展类加载为:" + extClassLoader);
            System.out.println("应用类加载器为:" + appClassLoader);
            System.out.println("***引导类加载器加载的文件有:***");
            URL[] urls = Launcher.getBootstrapClassPath().getURLs();
            for(URL url : urls){
                System.out.println(url);
            }
            System.out.println("***扩展类加载器加载的文件有:***");
            String extClassLoaderFiles = System.getProperty("java.ext.dirs");
            String[] extClassLoaderFilesArr = extClassLoaderFiles.split(";");
            for (String str : extClassLoaderFilesArr){
                System.out.println(str);
            }  
            System.out.println("***应用类加载器加载的文件有:***");
            String  appClassLoaderFiles = System.getProperty("java.class.path");
            String[] appClassLoaderFilesArr = appClassLoaderFiles.split(";");
            for (String str : appClassLoaderFilesArr){
                System.out.println(str);
            }   
        }
    }
    
    • 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

    结果验证了我们上文提到的关于不同类加载器加载的内容 ——

    ***引导类加载器加载的文件有:***
    file:/C:/Program%20Files/Java/jdk1.8.0_181/jre/lib/resources.jar
    file:/C:/Program%20Files/Java/jdk1.8.0_181/jre/lib/rt.jar
    file:/C:/Program%20Files/Java/jdk1.8.0_181/jre/lib/sunrsasign.jar
    file:/C:/Program%20Files/Java/jdk1.8.0_181/jre/lib/jsse.jar
    file:/C:/Program%20Files/Java/jdk1.8.0_181/jre/lib/jce.jar
    file:/C:/Program%20Files/Java/jdk1.8.0_181/jre/lib/charsets.jar
    file:/C:/Program%20Files/Java/jdk1.8.0_181/jre/lib/jfr.jar
    file:/C:/Program%20Files/Java/jdk1.8.0_181/jre/classes
    ***扩展类加载器加载的文件有:***
    C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext
    C:\Windows\Sun\Java\lib\ext
    ***应用类加载器加载的文件有:***
    C:\Program Files\Java\jdk1.8.0_181\jre\lib\charsets.jar
    C:\Program Files\Java\jdk1.8.0_181\jre\lib\deploy.jar
    C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\access-bridge-64.jar
    C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\cldrdata.jar
    C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\dnsns.jar
    C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\jaccess.jar
    C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\jfxrt.jar
    C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\localedata.jar
    C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\nashorn.jar
    C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunec.jar
    C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunjce_provider.jar
    C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunmscapi.jar
    C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunpkcs11.jar
    C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\zipfs.jar
    C:\Program Files\Java\jdk1.8.0_181\jre\lib\javaws.jar
    C:\Program Files\Java\jdk1.8.0_181\jre\lib\jce.jar
    C:\Program Files\Java\jdk1.8.0_181\jre\lib\jfr.jar
    C:\Program Files\Java\jdk1.8.0_181\jre\lib\jfxswt.jar
    C:\Program Files\Java\jdk1.8.0_181\jre\lib\jsse.jar
    C:\Program Files\Java\jdk1.8.0_181\jre\lib\management-agent.jar
    C:\Program Files\Java\jdk1.8.0_181\jre\lib\plugin.jar
    C:\Program Files\Java\jdk1.8.0_181\jre\lib\resources.jar
    C:\Program Files\Java\jdk1.8.0_181\jre\lib\rt.jar
    C:\Users\15141\Desktop\headCount\out\production\headCount
    D:\IntelliJ IDEA 2022.2.3\lib\idea_rt.jar
    Process finished with exit code 0
    
    • 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



    三、类加载双亲委派机制

    先向上委托找到父亲加载器加载,父加载器加载不了再下退请求再由儿子加载器自己加载,其流程如下:

    在这里插入图片描述

    好处:

    1. 沙箱安全机制 —— 比如自己写的 java.lang.String.class 类不会被加载,从而防止核心 API 类库被篡改
    2. 避免类重复加载:只由加载一次就行了,发现被加载过,直接返回



    四、Tomcat 底层类加载 —— 关于自定义类加载器

    1. Tomcat类加载用的是双委派机制嘛

    Tomcat 打破了双亲委派机制,原因如下:

    不同应用程序依赖的 war 包使用了相同名称的类,但是这些类版本不同。如果使用双亲委派机制,只能加载一个,不能把不同版本的都加载进去。

    再详细解释:

    我们都用过 Tomcat 服务器,一个 Tomcat 服务器上可能同时部署多个应用程序,这多个应用程序可能使用的第三方类库相同,但是版本不同,也就是说存在类名相同,但是内容不同的类。这时,假如我们使用了双亲委派机制,那就只能加载一个进去。很显然,这是不合理的,因此 Tomcat 使用自定义类加载器,而不使用双亲委派机制。

    Tomcat 通过自定义类加载器的方式解决上述问题,其示意图如下:

    在这里插入图片描述

    2. Tomcat自定义类加载器有那些:
    1. CommonLoader:Tomcat 最基本的类加载器,加载路径中的 class 可被 Tomcat 容器本身以及各个 Webapp 访问
    2. CatalinaLoader: Tomcat 容器私有的类加载器,加载路径中的 class 对于 Webapp 不可见
    3. ShareLoader:各个 Webapp 共享的类加载器,加载路径中的 class 对于所有的 Webapp 可见,对 Tomcat 容器不可见
    4. WebappClassLoader:各个 Webapp 私有的类加载器,加载路径中的 class 只对当前的 Webapp 可见,对其他的 Webapp 不可见 (实现每个 Web 应用可依赖不同版本的类的关键)

    在这里插入图片描述

  • 相关阅读:
    汽车租贷管理系统简单实现
    要不要提前去实习?
    maven
    【云原生监控系列第二篇】Prometheus进阶——PromQL数据类型
    go线程安全哈希表concurrent-map
    使用ssh上传数据到阿里云ESC云服务上
    Linux学习-76-LAMP环境搭建和LNMP环境搭建-前期准备
    XML配置文件的解析
    游戏物理引擎+特效系统
    软考高项 重要知识点整理
  • 原文地址:https://blog.csdn.net/liuwanqing233333/article/details/128148315