类加载和双亲委托机制

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

类与类加载器

对于任何一个类,都需要由加载它的类加载器和这个类来确立其在JVM中的唯一性。也就是说,两个类来源于同一个Class文件,并且被同一个类加载器加载,这两个类才相等。

双亲委派模型?

oracle 官方文档关于 jvm 类加载机制所用的描述是:

The Java platform uses a delegation model for loading classes. The basic idea is that every class loader has a "parent" class loader. When loading a class, a class loader first “delegates” the search for the class to its parent class loader before attempting to find the class itself.

翻译过来就是:

java 平台使用 委派模型来加载类。 基本思想就是, 每一个类加载器都有一个父加载器, 当需要加载一个 class时, 首先把该 class 的查询和加载优先委派给父加载器进行, 如果父加载器无法加载该 class, 再去尝试自行加载这个 class

JDK 默认提供了如下几种ClassLoader?

在这里插入图片描述

  1. Bootstrp loader

Bootstrp加载器是用C++语言写的,它是在Java虚拟机启动后初始化的,
它主要负责加载%JAVA_HOME%/jre/lib,-Xbootclasspath参数指定的路径
以及%JAVA_HOME%/jre/classes中的类rt.jar

  1. ExtClassLoader

Bootstrploader加载ExtClassLoader,并且将ExtClassLoader的父加载器设置为Bootstrploader
ExtClassLoader是用Java写的,具体来说就是sun.misc.Launcher$ExtClassLoader
ExtClassLoader主要加载%JAVA_HOME%/jre/lib/ext,此路径下的所有classes目录
以及java.ext.dirs系统变量指定的路径中类库。

  1. AppClassLoader

Bootstrploader加载完ExtClassLoader后,就会加载AppClassLoader,并且将AppClassLoader的父加载器指定为ExtClassLoader。AppClassLoader也是用Java写成的,它的实现类是sun.misc.Launcher$AppClassLoader
另外我们知道ClassLoader中有个getSystemClassLoader方法,此方法返回的正是AppclassLoader.AppClassLoader主要负责加载classpath所指定的位置的类或者是jar文档,它也是Java程序默认的类加载器。

在这里插入图片描述

双亲委派机制的工作流程?

1当前ClassLoader首先从自己已经加载的类中查询是否此类已经加载,如果已经加载则直接返回原来已经加载的类。
每个类加载器都有自己的加载缓存,当一个类被加载了以后就会放入缓存,等下次加载的时候就可以直接返回了。

2当前classLoader的缓存中没有找到被加载的类的时候委托父类加载器去加载父类加载器采用同样的策略,首先查看自己的缓存,然后委托父类的父类去加载,一直到bootstrp ClassLoader.

3.当所有的父类加载器都没有加载的时候再由当前的类加载器加载,并将其放入它自己的缓存中,以便下次有加载请求的时候直接返回。

在这里插入图片描述

在这里插入图片描述

从上图中我们就更容易理解了,当一个Hello.class这样的文件要被加载时。不考虑我们自定义类加载器,首先会在AppClassLoader中检查是否加载过,如果有那就无需再加载了。如果没有,那么会拿到父加载器,然后调用父加载器的loadClass方法。父类中同理也会先检查自己是否已经加载过,如果没有再往上。注意这个类似递归的过程,直到到达Bootstrap classLoader之前,都是在检查是否加载过,并不会选择自己去加载。直到BootstrapClassLoader,已经没有父加载器了,这时候开始考虑自己是否能加载了,如果自己无法加载,会下沉到子加载器去加载,一直到最底层,如果没有任何加载器能加载,就会抛出ClassNotFoundException。那么有人就有下面这种疑问了?

为什么要设计这种机制?优势是什么?

这种设计的好处是,避免重复加载 + 避免核心类篡改
避免重复加载:Java类随着它的类加载器一起具备了一种带有优先级的层次关系,通过这种层级关可以避免类的重复加载,当父亲已经加载了该类时,就没有必要子ClassLoader再加载一次。

避免核心类篡改如果有人想替换系统级别的类:String.java。篡改它的实现,在这种机制下这些系统的类已经被Bootstrap classLoader加载过了(为什么?因为当一个类需要加载的时候,最先去尝试加载的就是BootstrapClassLoader),所以其他类加载器并没有机会再去加载,从一定程度上防止了危险代码的植入。

这个写的系统且详细:https://blog.csdn.net/lengxiao1993/article/details/86689331

参考1:https://blog.csdn.net/lengxiao1993/article/details/86689331
参考2:https://blog.csdn.net/u010312474/article/details/91046318
参考3
参考4

说说为啥要打破双亲委派模型,如何打破?

JDBC之所以要破坏双亲委派模式是因为,JDBC的核心在rt.jar中由启动类加载器加载,而其实现则在各厂商实现的的jar包中,根据类加载机制,若A类调用B类,则B类由A类的加载器加载,也就是说启动类加载器要加载jar包下的类,我们都知道这是不可能的,启动类加载器负责加载$JAVA_HOME中jre/lib/rt.jar里所有的class,
那么JDBC是如何加载这些Driver实现类的?
通过Thread.currentThread().getContextClassLoader()得到线程上下文加载器来加载Driver实现类

还有就是我们可以自定义的加载器继承ClassLoad然后修改的loadClass和find classde 方法,从而可以打破双亲的委派机制

原文链接:https://blog.csdn.net/qq_42820805/article/details/113750352

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值