JVM深入浅出(8)--- 类加载器

类加载器最重要的功能就是加载的过程,也就是通过完全限定名去获得一个class对象的二进制流。

1. 类与类加载器

在jvm中,是通过类本身和加载类的类加载器来确认一个类的唯一性。每一个类加载器,都有它的类名称空间。

2. 双亲委派模型

类加载器(没有模块化之前):

  • 启动类加载器(Bootstrap classloader): C++实现的,是jvm的一部分,访问的时候返回值是null。
    • 启动类加载器负责加载 <JAVA_HOME>\lib 目录,或者被-Xbootclasspath参数所指定的路径中存放的
  • 扩展类加载器(Extension Class Loader)
    • 负责加载\lib\ext目录中,或者被java.ext.dirs系统变量所 指定的路径中所有的类库,可以在程序中直接使用该加载器加载
  • 应用程序类加载器(Application Class Loader):
    • 负责加载用户类路径 (ClassPath)上所有的类库,开发者同样可以直接在代码中使用这个类加载器。如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。
  • 自定义类加载器:用户可以通过重写loadclass()/findclass()方法实现

image-20260213200229826

双亲委派模型:各类加载器之间的关系组成了双亲委派模型,双亲委派模型除了启动类加载器,要求其他加载器都有自己的父加载器,不过这里的父子关系不是继承实现,通常是组合关系复用父加载器的代码

  • 双亲委派模型的工作过程:
    • 当一个加载器收到加载某个类的请求,先不自己去处理这个类的加载,而是将请求委派给父加载器去加载这个类,如果父加载器反馈无法加载这个类(它的搜索范围中没有找到所需的类),再自己来加载这个类。
  • 双亲委派模型的好处:
    • 保证了加载类的唯一性,就好比Object类,如果用户也写了个Object类,jvm由于双亲委派模型,永远都会用启动类加载器去加载这个类,也就确保了加载类的唯一性,保证java程序稳定运行。
//双亲委派模型的实现
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException
{
    // 首先,检查请求的类是否已经被加载过了
    Class c = findLoadedClass(name);
    if (c == null) {
        try {
        if (parent != null) {
            c = parent.loadClass(name, false);
        } else {
            c = findBootstrapClassOrNull(name);
        }
        } catch (ClassNotFoundException e) {
            // 如果父类加载器抛出ClassNotFoundException
            // 说明父类加载器无法完成加载请求
        }
        if (c == null) {
            // 在父类加载器无法加载时
            // 再调用本身的findClass方法来进行类加载
            c = findClass(name);
        }
    }
    if (resolve) {
        resolveClass(c);
    }
}

3.破坏双亲委派模型

  • 第一次破坏:由于双亲委派模型在JDK1.2推出,但是在这之前已经有了类加载器的概念,为了让用户能重写类加载器而不破坏双亲委派模型,鼓励开发者重写 findclass(),而不是loadclass()方法(里面有双亲委派寻找父加载器的逻辑)

  • 第二次破坏:父加载器加载的类,需要调用子加载器加载类中的API,这种父加载器肯定是不认识子加载器的类的,所以推出了线程上下文加载器

  • 第三次“被破坏”是由于用户对程序动态性的追求而导致的

posted @ 2026-04-07 17:42  不会coding的喵酱  阅读(0)  评论(0)    收藏  举报