JVM——深入理解类加载器

类与类加载器

类加载器虽然只应用于类的加载阶段,但它起到的作用远超于此,类加载器可以帮助类确定类的唯一性,相同的两个类(这里指两个类的equals等方法相同)被同一个虚拟机加载,只要他们的类加载器不同即使用的是同一个字节码文件,都会被虚拟机视为不同的两个类

双亲委派模型

对于虚拟机来说只有两类类加载器一种是Bootstrap ClassLoader是通过C++语言实现的,是虚拟机自身的一部分,另一类是其它类加载器,是使用Java语言实现的都继承自java.lang.ClassLoader

而对于开发人员来说,类加载器一直保持着三层类加载器、双亲委派模型的类加载架构

  • 启动类加载器(Bootstrap CLassLoader):属于虚拟机的一部分,通过C++实现的,负责加载存放在<JAVA_HOME>\lib目录,或者被-Xbootclasspath参数所指定的路径存放的类
  • 扩展类加载器(Extension ClassLoader):独立于虚拟机, 负责加载<JAVA_HOME>\lib\ext中的类库。java9之后由于模块化的需要被PlatFormClassLoader取代
  • 应用程序类(Application ClassLoader):独立于虚拟机,主要负责加载用户类路径(classPath)上的类库,如果没有实现自定义类加载器,那么这个这个加载器就是我们程序的默认类加载器

类加载器之间的关系,其中PlatformClassLoader是在java9之后出现的,java8之前一直是Extention ClassLoader替代PlatformClassLoader的位置

 

对于这种类加载器之间的层次关系被称为“双亲委派模型”,双亲委派模型要求除了顶层类加载器外,其余类加载器都要有自己的父类类加载器,这里的子类和父类之间的关系并不是继承关系,而是组合关系来服用父类加载器的代码

双亲委派模型的工作流程是,如果一个类加载器收到了类加载的请求,它首先并不会独自加载这个类,而是将类加载的请求委派给自己的父级类加载器,每一层级的类加载器都是如此,如果父类反馈自己无法完成这个类加载的请求,子类加载器才会去尝试加载这个类

双亲委派模型逻辑:

 

双亲委派模型的好处是,java内部的类都会随着类加载器而出现层级关系,例如如果有一个Object的对象,不管最开始是哪个类加载器加载的,最后都会委派给启动类加载器,因此Object在程序的任何一个类加载器中都能够保证是同一个类。反之如果不适用双亲委派模型,都由各个类加载器去加载的话,如果用户自己写了一个java.lang.Object的类并且放在了程序的ClasssPath中,那么程序中就会出现多个Object类,应用将会变得很混乱

破坏双亲委派模型

双亲委派模型并不是具有强制行约束的模型,而是java设计者们推荐给开发者的类加载方式,到现在java大多都遵循着这个模型,不过也有例外情况,历史上有三次比较大规模破坏双亲委派模型的情况

第一次被破坏

第一次被破坏时在双亲委派模型出现之前,双亲委派模型实在java1.2的时候才推出的,而类加载器和抽象类的该您在java的第一个版本就已经存在了,面对当时已存在的用户自定义类加载器的代码,java设计者们在引入双亲委派模型时,不得不做一些妥协,为了兼容这些代码,无法再以授权避免loadClass()被子类覆盖的可能,只能在java.lang.ClassLoader这个类中新加一个findClass()的方法,并引导用户在编写自定义类加载器时尽量去重写findClass()方法,毕竟重写loadClass()方法就可以破坏双亲委派模型的逻辑了,loadClass()方法的逻辑就是双亲委派模型的实现逻辑,按照loadClass()方法的逻辑,如果父类加载失败,会自动调用自己的findClass()方法来完成加载,这样既不影响用户按自己的医院去加载类,又可以保证新写出来的类加载器时符合双亲委派规则的

第二次被破坏

第二次被破坏是因为该模型自身的缺陷,双亲委派模型很好的解决了各个类加载器协作时基础类型的一致性问题(越基础的类型就越有上层的加载器来加载),基础类型之所以基础是,是因为它们总是作为被用户代码继承、调用的API存在,但如果有基础类型又要调用回用户的代码,那该怎么办呢

像比较熟的JDBC、JNDI就是如此,在java的核心包里定义了一个SPI,要求实现的厂商通过SPI来实现具体操作,核心包里的类就是由启动类加载器加载的,而它的手只能摸到\lib或者Xbootclasspath指定的路径中,其它的鞭长莫及,而JDBC、JNDI的实现类在用户定义的classpath中默认由ApplicationClassLoader加载,所以启动类加载器就需要委托子类来加载JDBC、JNDI的具体实现,违反了自下而上的委托机制

具体的解决办法就是加入一个线程上下文类加载器(Thread Class Loader),通过setContextClassLoader()默认是应用程序类加载器,然后利用Thread..current.currentThread().getContextClassLoader()获得类加载器来加载

第三次被破坏

这次破坏是为了满足热部署的需求,对于企业来说至关重要,OSGI就是自定义的类加载器完成模块化热部署,而他实现的类加载机制完全没有遵循自下而上的委托,有很多平级之间的委托加载

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值