【JVM】Java类加载器 和 双亲委派机制

1、java类加载器的分类

JDK8及之前

  • 启动类加载器,BootStrap Class Loader,加载核心类,加载jre/lib目录下的类,C++实现的
  • 拓展类加载器, Extension Class Loader,加载java拓展类库,jre/lib/ext目录下,比如javax,java.util包等
  • 应用程序类加载器,Application Class Loader, 也成系统类加载器,负责加载应用程序类,classpath下的类文件,maven依赖的类文件
  • 自定义加载器:继承ClassLoader,并重写findClass方法。

类加载器的作用?

负责在类的加载过程中,获取字节码文件并加载到内存中。通过加载字节码数据转换为byte[]数组,接下来调用虚拟机底层方法将byte[]数组转换成方法区和堆中的数据。

2、双亲委派机制
2.1 双亲委派机制为了解决什么问题?

核心是为了解决一个类到底是由哪个类加载器来加载

2.2 介绍一下双亲委派机制?

当一个类加载器需要去加载类时,会 首先委派给其父类加载器进行加载,如果父类加载器无法加载,才由该类加载器自己去加载

2.3、双亲委派机制有什么好处?

这种层级关系使得类加载器能实现类的共享,避免类被重复加载,

提高了代码的安全性和可靠性,避免恶意覆盖jdk的核心类库。

比如,你重写了java.lang.String类,因为双亲委派模型机制,jvm只会通过bootstrap类加载器加载jre/lib包下的String类,由于父类加载器已经加载过这个类,就不会重复加载自己定义的,这样一定程度上保障了jvm的安全。

2.4、双亲委派机制有什么缺点?

有时候,需要多次加载同名的目录下的类,比如,当在Tomcat上部署多个服务时,不同服务可能依赖了不同版本的第三方jar,如果使用双亲委派机制进行加载,那么多个服务中的第三方jar就只会加载一次,那么依赖另外一个版本的jar的服务就有可能产生异常。

为了解决这种情况,需要打破双亲委派机制,不在让父类加载器加载,而是每个服务创建自己的子类加载器。

3、双亲委派机制原理?

不多说,看代码

java.lang.ClassLoader

public Class<?> loadClass(String name) throws ClassNotFoundException {
    return loadClass(name, false);
}

protected Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException
{
    synchronized (getClassLoadingLock(name)) {
        // 能够查到类,就说明类已经被加载了,直接返回了。
        Class<?> c = findLoadedClass(name);
        if (c == null) {
            long t0 = System.nanoTime();
            try {
                // 双亲委派机制就在这里,如果父加载器不为空,就调用父加载器来尝试加载类
                if (parent != null) {
                    c = parent.loadClass(name, false);
                } else {
                    // 拓展类加载器的父加载器是null,
                    // 但是上层还有一个C++实现的启动类加载器
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {

            }

            // 父加载器没有加载到,就自己尝试加载
            if (c == null) {
                long t1 = System.nanoTime();
                // 加载方法,protected修饰的,因此如果自定义类加载器,不想破坏双亲委派模型的话,就重写这个方法
                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();
            }
        }
        // resolveClass方法就是类加载阶段的连接 阶段,loadClass方法是赋值为false的,不会进行连接
        if (resolve) {
            resolveClass(c);
        }
        return c;
    }
}

4、如何破坏双亲委派机制呢?
4.1:自定义类加载器,重写loadClass方法

但是如果只为了定义自定义加载器,建议重写findClass方法,这样不会破坏双亲委派机制。

public class MyClassLoader extends ClassLoader {
    @Override
    public Class<?> loadClass(String name) throws ClassNotFoundException {
        // 核心类库还是交给双亲委派机制进行加载
        if (name.startsWith("java.")) {
            super.loadClass(name);
        }
        
        // 通过类的全限定名,找到字节码文件,并转为byte数组 不写了
        byte[] data  = loadClassData(name);
        
        // 类的加载就是由defineClass这个底层方法实现的。
        return defineClass(name, data, 0, data.length);
    }
}

4.2 打破双亲委派机制的另外一种案例:JDBC

介绍:JDBC中使用了DriverManger来管理项目中引入的不同数据库的驱动,比如mysql,oracle驱动等。DriverManger这个类,位于rt.jar包中,由启动类加载器进行加载。但是DriverManger这个类是要去加载引入的jar包中的驱动类的,但是引入的jar包应该是由应用程序类加载器加载的,

这就出现了一个问题:启动类加载器加载完成DriverManger后,会委托应用类加载器去加载jar包中的驱动。这种委托关系就打破了双亲委派机制。

DriverManger怎么知道jar包中要加载的驱动在哪里?

spi机制(service provider interface),jdk内置的一种服务发现机制。查找classpath路径下的META-INF/services 文件夹下面的文件,并自动加载文件里所定义的类(这个文件中定义的就是需要加载的类的全限定名称).

这个文件中定义的就是需要加载的类的全限定名称,在JDBC中,比如mysql,oracle等第三方jar包驱动,就在META-INF/services 文件下面定义了以接口为名称的文件,文件内容就是对接口具体的实现类。

在springboot中,会引入一些其他依赖,自动注入的过程中,因为第三方jar包类和当前启动项不在一个包中,所以不能通过扫描进行注入,而且注入也要尽可能的和第三方依赖解耦。

springboot就规定了在META-INF文件夹中可以定义spring.factories文件,项目启动的时候会遍历所有jar包中的spring.factories,将里面定义的类加载到IOC中。

---- 上述就是springboot 中SPI机制的实现。

4.3 怎么理解JDBC案例中打破双亲委派机制?
  1.  首先是DriverManager这个类,是由启动类加载器加载的,毫无疑问符合双亲委派机制
  2. DriverManager加载驱动类时,不知道这些驱动类在哪里(比如引入了mysql或者oracle),就在初始化阶段的时候通过SPI机制去寻找,把加载这些类交给了应用类加载器去加载。(问题就出在这里了,启动类加载器委托应用类加载器去加载具体的驱动类。从这一层理解,好像是打破了双亲委派机制。)
  3. 但是启动类加载器知道要加载哪些类了之后,加载这些类的过程,还是符合双亲委派机制的。
  4. 通过上面的描述,JDBC是否打破双亲委派机制这个事情,见仁见智吧。

  • 13
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值