【Java基础】JVM双亲委派模型

一、什么是JVM的双亲委派模型?

        双亲委派模型是类加载器的一种工作机制,是Java设计者为了保证类的安全性和一致性而提出的一种规则(可以理解为类加载器为了加载类时保证类的安全性和一致性而设计的一种规则)。此规则的定义如下:

1、类加载器接收到要加载类的请求时,先判断该类是否已被加载;

2、如果已经加载,返回该类的Class对象;

3、如果未被加载过,类加载器不立即尝试自己加载,而是将请求向上交给自己的父类加载器进行加载;

4、父类加载器亦遵循此原则,逐级向上委托,直到Bootstrap ClassLoader(启动类加载器);

5、如果所有的类加载器都未找到并加载指定类,则由最初接受请求的类加载器加载该类。

     类加载器加载类,是指将类的.class文件读入内存,并为之创建能在jvm中运行的java.lang.Class对象的过程。

二、类加载器都有哪些?

1、启动类加载器;

        BootStrap ClassLoader,加载核心Java API库,如rt.jar;

2、扩展类加载器;

        Extension ClassLoader,负责加载标准扩展目录中的类库,如<JAVA_HOME>/lib/ext下的jar包或由其他系统变量java.ext.dirs指定位置的类库。

3、系统类加载器;

        Application ClassLoader,加载classpath环境变量所指定的路径下的类库,包括应用的CLASSPATH、项目类路径或通过Maven 、gradle等构建工具配置的依赖库。

        这里说一下,如果没有特殊自定义类加载器的情况下,工程中通过构建工具管理并打包到最终运行时的classpatch下的如rocketmq-client Jar包,一般由系统类加载器加载。如果需要自定义加载逻辑,可以使用自定义类加载器。

4、用户自定义类加载器;

        可以按需创建并继承系统类加载器或其他已存在的类加载器,他们通常用来加载特定路径或以特殊方式获取的类。

        使用自定义类加载器,可以打破双亲委派模型,不完全遵循从父加载器开始的委托加载机制。通常情况下,自定义类加载器会在其findClass()方法中找到并加载类,而不是调用loadClass()方法(此方法会遵循双亲委派模型),这样会打破双亲委派模型。

三、举例:双亲委派模型下加载由Maven管理并打包的rocketmq-client jar中的类,要经过哪些过程?

        1、当系统类加载器接受到加载rocketmq-client jar包中的类请求,会先向上委托给父类扩展类加载器。

        2、扩展类加载器检查发现此类不在本加载器的加载范围内(或文件格式有问题等解析错误)会向上交给父类启动类加载器。

        3、由于启动类加载器主要用于加载Java核心类库和平台类,无法加载rocketmq-client类库。然后请求被逐级回传最终回到系统类加载器

        4、系统类加载器会在自身的加载范围内查找和加载rocketmq-client jar包中的类。

        

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

        保证类加载的安全性和唯一性。在遵循双亲委派模型的情况下,不同版本的同一个类(类名+包名完全一致),只会被加载一次。因为类加载器接受到请求时,会先检查此类是否已经被加载过,如果已加载过,直接返回该类的Class对象,不会重复加载。

        通过双亲委派机制,Java最核心的类库如java.lang.*等只能交给最可信的Bootstrap ClassLoader加载,防止用户自定义加载器错误的加载核心api或替换系统类,从而保证了系统的安全性和稳定性。

五、触发双亲委派的场景有哪些?

        换句话说,类加载器什么时候收到加载类的请求?或者jvm什么时候需要加载类?

        1. 遇到new关键字创建类实例:

        当Java程序使用new关键字创建一个类的新实例时,会触发该类的加载和初始化过程。

        2. 访问静态成员(字段或方法):

        首次主动引用类的静态字段(除final常量外)或者调用静态方法时,需要确保对应的类已经加载并初始化完成。

        3. 反射调用:

        使用Class.forName()、ClassLoader.loadClass()等方法进行反射操作时,如果类还未被加载,则会触发类加载。

        4. 初始化子类:

        当初始化某个类的时候,如果发现其父类还没有初始化,则先触发父类的初始化。

        5. 启动类加载器加载初始类:

        JVM启动时,会指定一个包含main()方法的主类作为初始类进行加载。

        6. 接口的初始化:

        实现类初次调用接口中的静态方法时,会触发接口的加载与初始化。

六、打破双亲委派模型

        在聊到双亲委派模型时都会聊到打破双亲委派,那什么时候需要打破双亲委派呢?换句话说,为了实现什么目的可以不遵循双亲委派机制呢?(因为制定的规则被打破,大概率是为了解决某些问题或是此规则不满足所有情况)

        打破双亲委派意味着不遵循双亲委派模型,按照需求直接加载这个类,比如在Tomcat,Jetty等servlet服务器为了支持每个应用有独立的类加载结构(每个应用可以使用不同的库版本),为每个应用创建了自定义的类加载器,并且这些类加载器在加载Web应用相关的类时,并不会总是优先委托给父加载器,从而实现了类库的隔离。

        另外,在Spring框架的一些模块中,如Spring Boot Devtools中的RestartClassLoader,在热部署的情况下也会打破双亲委派模型,以便重新加载已修改的类。

总结:

        所以,双亲委派模型是jvm为类加载器加载类保证类的唯一性和安全性而制定的规则,类加载器接受到请求时,如果该类未被加载,会逐级向上委派到父类。父类不能加载时(不在父类加载的范围内),再逐级回传到最初接受请求的类加载器。而打破双亲委派模型,比如为了加载不同版本的同一个类,会由自定义类加载器直接从指定路径加载类,而不是向上传递。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小王师傅66

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值