【JVM】详解类加载机制

一、类的生命周期

对于一个类来说,它的生命周期是这样的:
在这里插入图片描述

当程序主动使用某个类时,如果该类还未被加载到内存中,则JVM会通过加载连接初始化三个步骤对该类进行初始化。所以将这三个步骤称为类加载
其中连接又分为三步:验证、准备、解析

下面我们来仔细了解一下类加载的过程:

二、类加载的过程

1.加载

  1. 加载是将类的class文件读入到内存,并将这些静态数据转换成方法区中的运行时数据结构,并在堆中生成一个代表这个类的java.lang.Class对象,作为方法区类数据的访问入口,这个过程需要类加载器参与。

2.连接

当类被加载之后,系统会为之生成一个对应的Class对象,接着将会进入连接阶段,连接阶段负责把类的二进制数据合并到JRE中(意思就是将java类的二进制代码合并到JVM的运行状态中)。

类连接又可分为以下三个阶段:

  1. 验证:确保加载的类信息是否符合JVM规范,有没有安全方面的问题。主要验证是否符合Class文件格式规范,并且是否能被当前的虚拟机加载处理。
  2. 准备:正式为类中定义的变量(静态变量)分配内存并设置类变量默认初始值阶段,这些内存都将再方法区中进行分配。
  3. 解析:虚拟机常量池的符号引用替换成直接引用的过程,也就是初始化变量的过程。 符号引用:是一组符号来描述所引用的目标,符号可以是任何的字面形式的字面量,只要不会出现冲突能够定位到就行。布局和内存无关。 直接引用:是指向目标的指针,偏移量或者能够直接定位的句柄,该引用是和内存中的布局有关的,并且一定加载进来的。

3.初始化

初始化是为类的静态变量赋予正确的初始值,准备阶段和初始化阶段看似有点矛盾,其实不然。 比如这样的一句代码:private static int a=10;,在准备阶段给a赋值是int类型的默认初始值0,到初始化这一阶段才会把a真正的值10赋给它。

三、类加载器的介绍

类加载器就是在类加载阶段实现“通过一个类的全限定名(包名+类名)来获取类的二进制字节流”这个动作的。

在这里插入图片描述

3.1 启动类加载器(根类加载器/引导类加载器)(Bootstrap ClassLoader)

它用来加载Java的核心类,是用原生代码来实现的,并不继承自java.lang.ClassLoader.由于启动类加载器涉及到虚拟机本地实现细节,开发者无法直接获取到启动类加载器的引用,所以不允许直接通过引用进行操作。负责加载扩展类加载器和系统类加载器,并为他们的指定父类加载器。

3.2 扩展类加载器

由Java语言编写的,派生于ClassLoader类,上层类加载器为启动类加载器。它负责加载JRE/lib/ext目录下的类。

3.3 系统类加载器

Java语言编写,派生于ClassLoader类,上层类加载器为扩展类加载器。负责加载我们自己定义的类。

四、双亲委派模型

工作原理:如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类加载器去执行,如果费雷加载器还存在其父类加载器,则进一步向上委托,一次递归,请求最终将到达顶层的启动类加载器。如果父类加载器可以完成类加载任务,就成功返回,如果父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,这就是双亲委派模式。
也就是每个儿子都很懒,每次有活交给父亲去干,直到父亲说这件事我也干不了的时候,儿子才自己想办法。

在这里插入图片描述

4.1 双亲委派模型的优点

  • 避免类的重复加载:比如A类和B类都有一个父类C类,那么A启动时就会将C类加载起来,那么B类进行加载的时候就不需要重复加载C类了。
  • 安全性:使用双亲委派模型可以保证Java核心API不被篡改。假设通过网络传递一个名为java.lang.Integer的类,通过双亲委派莫辛纳甘传递到启动类加载器,而启动类加载器在核心Java API发现这个名字的类,发现该类已经被加载了,并不会重新加载网络传递过来的java.lang.Integer类,而是直接返回已加载过的Integer.class,这样便可以防止核心API库被随便篡改。

4.2 双亲委派模型的破坏

双亲委派模型的弊端:不能向下委派,不能不委派。
那么我们要打破双亲委派模型:也就是能向下委派和不委派。

向下委派:SPI机制

SPI机制
SPI机制是一种服务发现机制。它通过在ClassPath路径下的META-INF/services文件夹查找文件,自动加载文件里定义的类。这一机制为很多框架扩展提供了可能,比如在JDBC中就使用到了SPI机制。

SPI机制如何打破双亲委派模型:
在某些情况下父类加载器需要委托子类加载器去加载class文件。受加载范围的限制,父类加载器无法加载到需要的文件。
以Drive接口为例,DriverManger通过启动类加载器加载进来,而com.mysql.jdbc.Driver是通过系统类加载器加载进来的。由于双亲委派模型父类加载器是拿不到通过子加载器加载的类的。这个时候就需要启动类加载器来委托子类加载器来加载Driver实现,从而破坏了双亲委派模型。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值