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类。
相关推荐
2401_884563241 小时前
Python Lambda(匿名函数):简洁之道
jvm·数据库·python
庞轩px2 小时前
MinorGC的完整流程与复制算法深度解析
java·jvm·算法·性能优化
庞轩px2 小时前
内存区域的演进与直接内存——JVM性能优化的权衡艺术
java·jvm·笔记·性能优化
m0_730115113 小时前
自动化机器学习(AutoML)库TPOT使用指南
jvm·数据库·python
2301_807367194 小时前
Python日志记录(Logging)最佳实践
jvm·数据库·python
2301_795741794 小时前
构建一个基于命令行的待办事项应用
jvm·数据库·python
Java成神之路-4 小时前
深入 JVM:G1 垃圾回收器原理与实现细节
java·jvm
sw1213894 小时前
Python字典与集合:高效数据管理的艺术
jvm·数据库·python
m0_738098025 小时前
使用Python操作文件和目录(os, pathlib, shutil)
jvm·数据库·python
庞轩px5 小时前
缓存Key设计的“七要七不要”
java·jvm·redis·缓存