JVM 类加载器

目录

一、类加载过程

运行阶段,JVM中的类加载器读取.class文件,在方法区中创建该类的Class对象用于反射操作,对象中保存了类的格式和方法的字节码指令 ,。如果该类的父类还没有被加载,那么会首先加载父类

JVM运行时并不会一次性加载所有的类,而是根据需要动态请求类加载 。每个类加载器都有一个HashMap,记录<全限定类名,Class对象>,所以对同一个ClassLoader,二进制名称相同的类只会被加载一次

  • BootstrapClassLoader(启动类加载器):最顶层的加载类,由 C++实现,用来加载JDK的核心类库(%JAVA_HOME%/lib目录下)。
  • AppClassLoader(应用程序类加载器):负责加载当前应用中的所有自定义jar包和类(项目根目录下)。

二、双亲委派模型

对于同一个ClassLoader来说,同一个类只能被加载一次,但是不同的加载类可以加载同一个类,这就有类的重复加载问题 。双亲委派模型会先将类加载请求转发给父类加载器,确保了不会有重复加载问题。在父类加载器没有找到所请求的类的情况下,该类加载器才会尝试去加载。

此外,双亲委派模型还能防止核心API被篡改 ,当项目中有多个String.class类时,委派BootstrapClassLoader加载JDK中的String类防止AppClassLoader加载自定义的String类。

类加载过程如下:

  • 类加载时首先判断当前类是否被加载过,已经被加载的类会直接返回,否则才会尝试加载。
  • 类加载器在进行类加载的时候,它首先不会自己去尝试加载这个类,而是把这类加载请求委派给父加载器去完成 (调用父加载器loadClass()方法来加载类),这样的话,所有的请求最终都会传送到顶层的启动类加载器BootstrapClassLoader中。
  • 类加载器加载时首先判断父类是否被加载,如果没有则先加载父类
  • 只有当父加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去加载(调用自己的findClass()方法来加载类)。
  • 如果子类加载器也无法加载这个类,那么它会抛出一个ClassNotFoundException异常。
java 复制代码
protected Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException
{
    synchronized (getClassLoadingLock(name)) {
        //首先,检查该类是否已经加载过
        Class c = findLoadedClass(name);
        if (c == null) {
            //如果 c 为 null,则说明该类没有被加载过
            long t0 = System.nanoTime();
            try {
                if (parent != null) {
                    //当父类的加载器不为空,则通过父类的loadClass来加载该类
                    c = parent.loadClass(name, false);
                } else {
                    //当父类的加载器为空,则调用启动类加载器来加载该类
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
                //非空父类的类加载器无法找到相应的类,则抛出异常
            }

            if (c == null) {
                //当父类加载器无法加载时,则调用findClass方法来加载该类
                //用户可通过覆写该方法,来自定义类加载器
                long t1 = System.nanoTime();
                c = findClass(name);

                //用于统计类加载器相关的信息
                sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                sun.misc.PerfCounter.getFindClasses().increment();
            }
        }
        if (resolve) {
            //对类进行link操作
            resolveClass(c);
        }
        return c;
    }
}

三、自定义类加载器

由于AppClassLoader只能加载项目内的类,如果我们想加载项目外的类,默认的ClassLoader就不能满足我们的需求了,所以需要定义自己的ClassLoader。

继承ClassLoader抽象类,重写以下方法:

  • findClass(String name):给定要加载的类的二进制名称,重写该方法后默认还是双亲委派机制先调用父加载器loadClass()方法加载,如果加载失败才调用该方法内的逻辑加载。
  • loadClass(String name, boolean resolve):重写该方法后类加载器会首先尝试自己加载name类。
相关推荐
姓蔡小朋友4 小时前
JVM 内存分区
jvm
成为你的宁宁5 小时前
【Zabbix运维监控实战(附图文教程):Nginx 服务可用性、连接请求状态、CPU 内存占用与 JVM(Jar 包 / Tomcat)全维度监控】
运维·jvm·nginx·zabbix
姓蔡小朋友5 小时前
JVM 垃圾回收
jvm
杨杨杨大侠7 小时前
深入理解 LLVM:从编译器原理到 JIT 实战
java·jvm·编译器
鱼跃鹰飞8 小时前
怎么排查线上CPU100%的问题
java·jvm·后端
小当家.1058 小时前
JVM/八股详解(下部):垃圾收集、JVM 调优与类加载机制
java·jvm·面试
芒克芒克9 小时前
JVM性能监控
java·jvm
码农阿豪10 小时前
远程调试不再难!Remote JVM Debug+cpolar 让内网 Java 程序调试变简单
java·开发语言·jvm
程序猿20231 天前
MAT(memory analyzer tool)主要功能
jvm