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类。
相关推荐
马猴烧酒.1 小时前
【DDD重构|第十三天】DDD 领域驱动设计详解+实战
java·jvm·ide·重构·tomcat·maven·团队开发
筷乐老六喝旺仔1 小时前
使用Python进行PDF文件的处理与操作
jvm·数据库·python
C+-C资深大佬1 小时前
C++多态
java·jvm·c++
今天你TLE了吗1 小时前
JVM学习笔记:第一章——JVM&Java体系结构
java·jvm·笔记·学习
yufuu982 小时前
进阶技巧与底层原理
jvm·数据库·python
2301_817497332 小时前
使用Flask快速搭建轻量级Web应用
jvm·数据库·python
hgz07102 小时前
堆内存分区
java·开发语言·jvm
丁劲犇3 小时前
老旧CentOS7服务器JVM加载Jar缓慢排查:竟与NTP服务器有关
服务器·jvm·jar·java8·ntp·加载慢
一起养小猫12 小时前
Flutter for OpenHarmony 实战:记账应用数据统计与可视化
开发语言·jvm·数据库·flutter·信息可视化·harmonyos
爱学习的阿磊14 小时前
使用PyTorch构建你的第一个神经网络
jvm·数据库·python