• 类加载器、双亲委派机制


    1 JVM是什么

    Java Virtual Machine(Java虚拟机)是java程序实现跨平台的⼀个重要的⼯具(部件)

    HotSpot VM,相信所有Java程序员都知道,它是Sun JDK和OpenJDK中所带的虚拟机,也是

    ⽬前使⽤范围最⼴的Java虚拟机。

    只要装有JVM的平台,都可以运⾏java程序。那么Java程序在JVM上是怎么被运⾏的?

    通过介绍以下JVM的三个组成部分,就可以了解到JVM内部的⼯作机制

    • 类加载系统:负责完成类的加载

    • 运⾏时数据区:在运⾏Java程序的时候会产⽣的各种数据会保存在运⾏时数据区

    • 执⾏引擎:执⾏具体的指令(代码)

    2 类加载系统

    类加载器是一个负责加载类的对象。ClassLoader 是一个抽象类。给定类的二进制名称,类加载器应尝试定位或生成构成类定义的数据。典型的策略是将名称转换为文件名,然后从文件系统中读取该名称的“类文件“。每个 Java 类都有一个引用指向加载它的 ClassLoader。不过,数组类不是通过 ClassLoader 创建的,而-是 JVM 在需要的时候自动创建的,数组类通过getClassLoader()方法获取 ClassLoader 的时候和该数组的元素类型的 ClassLoader 是一致的。

    简单来说,类加载器的主要作用就是加载 Java 类的字节码( .class 文件)到 JVM 中(在内存中生成一个代表该类的 Class 对象

    2.1 类的加载过程

    ⼀个类被加载进JVM中要经历哪⼏个过程

    • 加载: 通过io流的⽅式把字节码⽂件读⼊到jvm中(⽅法区)

    • 校验:通过校验字节码⽂件的头8位的16进制是否是java魔数cafebabe

    • 准备:为类中的静态部分开辟空间并赋初始化值

    • 解析:将符号引⽤转换成直接引⽤。——静态链接

    • 初始化:为类中的静态部分赋指定值并执⾏静态代码块。

    类被加载后,类中的类型信息、⽅法信息、属性信息、运⾏时常量池、类加载器的引⽤等信息会被加载到元空间中。

    2.2 类加载器

    类是谁来负载加载的?——类加载器。不同级别的类由不同的加载器加载

    • Bootstrap ClassLoader 启动类加载器:负载加载jre/lib下的核⼼类库中的类,⽐如rt.jar、charsets.jar

      我们常用内置库 java.xxx.* 都在里面,比如java.util.、java.io.、java.nio.、java.lang.、java.sql.、java.math.

    • ExtClassLoader 扩展类加载器:负载加载jre/lib下的ext⽬录内的类

      ext 加载路径:System.getProperty("java.ext.dirs");
      
      • 1
    • AppClassLoader 应⽤类加载器:负载加载⽤户⾃⼰写的类,加载当前应用 classpath 下的所有 jar 包和类。

      app 加载路径:System.getProperty("java.class.path");
      
      • 1
    • ⾃定义类加载器:⾃⼰定义的类加载器,可以打破双亲委派机制。

    在这里插入图片描述

    由图可知,上一层加载器都会以父类的形式传入下一次加载。

    3 双亲委派机制

    3.1 双亲委派机制介绍

    ​ 当类加载进⾏加载类的时候,类的加载需要向上委托给上⼀级的类加载器,上⼀级继续向上委托,直到启动类加载器。启动类加载器去核⼼类库中找,如果没有该类则向下委派,由下⼀级扩展类加载器去扩展类库中,如果也没有继续向下委派,直到找不到为⽌,则报类找不到的异常。

    双亲委派机制的流程如下:

    类加载的核心代码如下:

    ClassLoader.class中的

    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
    • 37
    • 38

    3.2 双亲委派机制的优缺点

    优点:

    • 避免重复加载:通过委派给父类加载器,可以避免同一个类被多次加载,提高了加载效率。

    • 安全性:通过双亲委派机制,核心类库由根加载器加载,可以确保核心类库的安全性,防止恶意代码替换核心类。

    • 扩展性:开发人员可以自定义类加载器,实现特定的加载策略,从而扩展Java的类加载机制。

    缺点:

    • 灵活性受限:双亲委派机制对于某些特殊的类加载需求可能过于严格,限制了加载器的灵活性。

    • 破坏隔离性:如果自定义类加载器不遵循双亲委派机制,可能会破坏类加载的隔离性,导致类冲突或安全性问题。

    • 不适合动态更新:由于类加载器在加载类时会先检查父加载器是否已加载,因此在动态更新类时可能会出现问题,需要额外的处理。

    总体而言,双亲委派机制通过层次结构和委派机制提供了一种有序、安全的类加载方式,但也存在一些限制和不适用的情况。

    3.3 自定义类加载器实现双亲委派机制

    有上面的源码可以看到,loadClass方法中会先看父类加载器中能不能调用,都不能在走findClass()方法,调用自己的加载器。可见 自定义类加载器主要就是重写findClass方法,再此类中,loadClass方法没有被重写,就会执行父类ClassLoader中的loadClass方法,只要执行的是ClassLoader中的方法,就是实现的双亲委派。

  • 相关阅读:
    玩转gpgpu-sim 04记—— __cudaRegisterBinary() of gpgpu-sim 到底做了什么
    重温以太坊的升级之路
    面试中的压力测试:如何稳定自己的心态
    Golang中defer与return的执行顺序——不易混淆
    gitlab 12.7恢复
    RabbitMQ常见命令总结
    一套成熟的ERP系统,应具备哪些能力?
    一致性 Hash 算法
    Thymeleaf
    java-net-php-python-ssm公司人力资源管理系统计算机毕业设计程序
  • 原文地址:https://blog.csdn.net/qq_43331014/article/details/133720739