JVM 双亲委派模型

类加载的过程

JVM获取到class文件后,就要通过类加载器把文件加载到内存的运行时数据区中。类的生命周期如下图所示。类加载用来负责class文件的加载,至于能不能执行,就不归它管。类加载包括三个过程:加载、连接(验证、准备、解析)、初始化。下面简单介绍类加载的过程做了什么事。

加载:通过一个类的全限定名获取定义此类的二进制字节流,将该字节流所代表的静态存储结构转换为方法区的运行时数据结构,然后生成一个代表该类的反射对象,作为方法区的这个类的各种数据的访问入口。

验证:确保class文件中的字节流是不是符合vm的要求,保证被加载类的正确性。

准备:为类变量分配内存且设置该变量的默认初始值。

解析:将常量池内符合引用转换成直接引用的过程。

初始化:jvm真正开始执行类中编写的Java程序代码。根据程序编码制定的主观计划去初始化类变量和其他资源。

类加载器

通过一个类的全限定名来获取描述该类的二进制字节流的这个动作的代码被称为“类加载器”。类加载器只用于实现类的加载动作。

站在JVM的角度,只存在两种不同的类加载:启动类加载器和自定义类加载类。

启动类加载器 Bootstrap ClassLoader

也叫引导类加载器。这个类加载器使用C++实现,是虚拟机的一部分。

这个类加载器负责加载存放在<JAVA_HOME>\lib目录,或者被-Xbootclasspath参数所指定的路径中存放的,而且是Java虚拟机能够识别的(按照文件名识别,如rt.jar、tools.jar,名字不符合的类库即使放在lib目录中也不会被加载)类库加载到虚拟机的内存中。

 启动类加载器无法被Java程序直接引用。需要通过双亲委派来间接引用。、

简单来说,该加载器用来加载Java的核心库,提供JVM自身需要的类,只能加载Java,Javax,sun等开头的类。

其他类加载器

由Java语言实现,独立存在虚拟机外,全部继承于抽象类java.lang.ClassLoader。

扩展类加载器

使用Java实现。是一种Java系统类库的扩展机制,比如自己将通用性的类放在相关的文件夹下来扩展JavaSE的功能。

应用类加载器

也称为系统类加载器。负责加载classpath上的所有类库。也就是说,它可以加载我们自己写的代码以及使用第三方的jar包。一般情况下,如果应用程序中,没有自己自定义过的类加载器,它就是程序中默认的类加载器。

如果自己认为还有必要可以加入自定义的类加载器来进程扩展。

整个加载器的关系如下图所示。

 双亲委派模型

前面我们说过,类加载器之间是存在继承的关系的。但是这种继承并不是Java语言中的“继承”,而是通过组合来实现。这种继承的关系是固定的。

委派过程

JVM对class文件采用按需加载的方式。当使用到该文件的时候,才会将它加载到内存中生成class文件。如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父 类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到最顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去完成加载。

优点

避免类的重复加载。比如A类和B类都有一个父类C类,那么当A启动时就会将C加载起来,那么在B类进行加载时就不需要在重复加载C类了。

能够保护程序,避免核心API被修改。Java中的类随着它的类加载器一起具备了一种带有优先级的层次关系。例如类java.lang.Object,无论哪一 个类加载器要加载这个类,最终都是委派给处于模型最顶端的启动类加载器进行加载,因此Object类在程序的各种类加载器环境中都能够保证是同一个类。反之,如果没有使用双亲委派模型,都由各个类加载器自行去加载的话,如果用户自己也编写了一个名为java.lang.Object的类,并放在程序的ClassPath中,那系统中就会出现多个不同的Object类,Java类型体系中最基础的行为也就无从保证,应用程序将会变得一片混乱。

破坏双亲委派

双亲委派模型并不是一个具有强制性约束的模型,而是Java设计者推荐给开发者们的类加载器实现方式。

有两种常见的方式可以打破双亲委派模型:

通过SPI机制,使用ServiceLoader.load去加载。SPI(Service Provider Interface)是一种服务发现和加载的机制,它允许用户为一些接口提供不同的实现,并通过配置文件指定具体的实现类。在SPI机制中,会使用当前线程上下文中的类加载器来加载SPI接口的实现类,而不是遵循双亲委派模型。
通过自定义类加载器,继承ClassLoader,重写loadClass方法。在loadClass方法中,可以自己定义类加载的逻辑和顺序,比如先尝试自己去加载某些特定的类,如果失败了再委托给父类加载器,或者直接忽略父类加载器而使用其他的类加载器等。这样就可以覆盖或者代理已经被父类加载器加载过的类。

打破双亲委派模型有一定的风险和注意事项:

可能会导致同一个类被不同的类加载器多次加载,造成内存浪费和类型不兼容等问题。
可能会破坏Java核心库的安全性和完整性,如果用户自定义的类与Java核心库中同名的类产生冲突或者篡改。
可能会影响其他框架或者容器对于双亲委派模型的依赖和预期行为。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值