java 类加载器

本文介绍了Java虚拟机如何通过类加载器加载类的过程,包括启动类加载器、扩展类加载器和应用程序类加载器的作用,以及双亲委派模型的工作原理和特殊情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

虚拟机设计团队把类加载阶段中的“通过一个类的权限定名来获取描述此类的二进制字节流”这个动作放到Java虚拟机外部去实现,以便让应用程序自己决定如何去获取所需要的类。实现这个动作的代码模块成为“类加载器”。

在Java虚拟机中,任意一个类,都需要加载它的类加载其和这个类本身一同确定其在Java虚拟机中的唯一性。否则,即使两个类来源同一个Class文件,被同一个虚拟机加载,只要加载它们的类加载器不同,那么这两个类就必定不相等。

从java虚拟机的角度,只存在两种不同的类类加载器:一种是启动类加载器,是虚拟机自身的一部分,另一种就是所有其他类的加载器,有Java语言实现,独立于虚拟机外部,并且全部都继承自抽象类 java.lang.ClassLoader..


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;
        }
    }

从java开发人员的角度,绝大部分程序都会使用到3种类加载器:

启动类加载器 开发人员不可以使用这个加载器。
扩展类加载器 负责加载lib\ext目录中的类,或者被java.ext.dirs系统变量指定的路径中的类。
应用程序类加载器(系统加载器):负责加载用户类路劲上的类。

类加载器之间的层次关系如下图,这种模型成为双亲委派模型。

这里写图片描述

双亲委派模型要求除了顶层的启动类加载器外,其余的类加载器应当有自己的父类加载器。这里类加载器之间的父子关系一般不会以继承的方式来实现,而是以组合的方式来实现。

双亲委派模型的工作过程:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载去完成,每一个层次的类加载器都是如此,一次所有的加在请求最终都是应该传送到顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求,子加载器才会尝试自己去加载。

破坏双亲委派模型:

双亲委派模型并不是一个强制性的约束模型,双亲委派模型也出现过3次较大规模的“破坏”。

双亲委派模型是jdk1.2引入的, 而类加载器和ClassLoader在此之前就存在,因此用户自定义的类就会重写loadClass 方法,而loadClass方法则是双亲委派模型的实现,重写以后就不存在双亲委派模型模型了。为了向前兼容,即处理上述这种情况,ClassLoader中增加一个findClass方法,用户继承ClassLoader,并将加载的逻辑写到findClass中即可,
在代码中

 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();
                }

如果在父类加载器中无法加载时,就会调用findClass 了。

这样就不会破坏双亲加载模型了。

双亲委派模型很好的解决了各个类加载器的基础类的统一问题,基础类一般是被用户代码调用,但是如果基础类又要调用回用户的代码,该怎么办?
JNDI服务就是这种场景,JNDI有启动类加载器加载,但是它需要地资源进行集中管理和查找,需要调用由独立厂商实现并部署在应用程序的ClassPath下的JNDI接口提供者的代码,这些代码,启动类加载器就不能加载了。

JNDI通过一种线程上下文加载器去加载所需要的SPI代码,也就是父类加载器请求子类加载器去完成类加载的动作,这么这种行为是和双亲委派模型相悖的。

双亲委派模型的第三次“被破坏”是由于用户对程序动态性的追求,java模块化标准 OSGI实现的模块化热部署,是通过它自定义的类加载器机制实现的。这个类加载是复杂的网状结构,而非双亲委派模型模型中的树状结构。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值