来聊聊JVM中的类加载过程以及双亲委派模型(学习Java必知内容)

文章目录


1. 类加载过程

在整个 JVM 执行过程中, 和我们程序员关系最密切的就是类加载的过程, 所以接下来我们来看下类加载的执行流程.

对于一个类来说, 它的生命周期是这样的:

其中前 5 步是固定的顺序并且也是类加载的过程, 其中中间的 3 步我们都属于连接, 所以对于类加载来说总共分为以下几个步骤:

  1. 加载
  2. 验证
  3. 准备
  4. 解析
  5. 初始化

类加载, 是一个非常复杂的过程, 此处我们只是简单介绍一下类加载的大致流程. 细致解析需到 Java 官方文档进行查阅. 下面我们来看看每个步骤的具体执行内容.

加载

"加载"(Loading)阶段指的是整个"类加载"(Class Loading)过程中的一个阶段,在加载 Loading 阶段,Java虚拟机需要完成以下三件事情:

  1. 通过一个类的全限定名来获取定义此类的二进制字节流。
  2. 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
  3. 在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。

通俗来说, 就是找到 .class 文件, 打开文件, 读取文件内容, 并尝试解析里面的格式.

验证

验证这一阶段的目的是, 确保 .class 文件的字节流中包含的信息符合《Java虚拟机规范》的全部约束要求,保证这些信息被当作代码运行后不会危害虚拟机自身的安全。

验证选项:

  • 文件格式验证
  • 字节码验证
  • 符号引用验证
  • ...

准备

准备阶段是正式为类中定义的变量(即静态变量,被static修饰的变量)分配内存并设置类变量初始值的阶段。最终的目标是构造出完整的类对象。

比如此时有这样一行代码:

public static int value = 123;

它在这个阶段 value 的 int 值是赋为 0,而非 123。

解析

解析阶段是 Java 虚拟机将常量池内的符号引用替换为直接引用的过程,也就是初始化常量的过程。

主要是初始化类中涉及到的一些字符串常量。

初始化

初始化阶段,Java 虚拟机真正开始执行类中编写的 Java 程序代码,将主导权移交给应用程序。初始化阶段就是执行类构造器方法的过程。

也就是这个环节,开始对类对象进行更具体的初始化操作。比如初始化静态成员,执行静态代码块,加载父类等等。

2. 双亲委派模型

谈到类加载机制, 不得不提的一个概念就是 "双亲委派模型".

双亲委派模型, 可以理解为就是类加载中 JVM 如何去找 .class 文件的一个过程.

JVM 加载 .class 文件时, 会使用到 "类加载器" 模块, JVM 中自带了三个类加载器.

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

一个类的加载流程

  1. 首先, 会从 Application ClassLoader 开始. 但不会立即就搜索第三方库的目录, 而是先把加载任务委派给父类, 让父类先尝试加载.
  2. 到了 Extension ClassLoader, 也不会立即就搜索扩展库的目录, 也会把加载任务委派给父类, 也让父类尝试加载.
  3. 到了 Bootstrap ClassLoader, 也想着把任务委派出去, 可是 Bootstrap ClassLoader 已经没有父类了, 只能自己动手搜索类了. 如果找到了这个类, 就会进行后续的加载. 如果没找到这个类, 就会将这个任务交回给子类加载器去完成.
  4. 任务回到了 Extension ClassLoader, 此时收到父类交回的任务后, 就会开始搜索扩展库的目录, 看看有没有匹配的 .class 文件. 如果找到了这个类, 就会进行后续的加载. 如果没找到这个类, 就会将这个任务交回给子类加载器去完成.
  5. 任务回到 Application ClassLoader, 此时就会开始搜索第三方库的目录(往往是自己开发时的项目目录), 如果找到了这个类, 就会进行后续的加载. 如果没找到这个类, 这时就会抛出异常.

双亲委派模型的优点

  1. 避免重复加载类:比如 A 类和 B 类都有一个父类 C 类,那么当 A 启动时就会将 C 类加载起来,那么在 B 类进行加载时就不需要在重复加载 C 类了。
  2. 安全性 :使用双亲委派模型也可以保证了 Java 的核心 API 不被篡改,如果没有使用双亲委派模型,而是每个类加载器加载自己的话就会出现一些问题,比如我们编写一个称为 java.lang.Object 类的话,那么程序运行的时候,系统就会出现多个不同的 Object 类,而有些 Object 类又是用户自己提供的因此安全性就不能得到保证了。

总结

✨ 本文主要讲解了 JVM 中的类加载过程, 以及其中涉及到的双亲委派模型, 是我们学习 Java 过程中, 需要着重了解的.

✨ 想了解更多知识, 请持续关注博主, 本人会不断更新学习记录, 跟随我一起不断学习.

✨ 感谢你们的耐心阅读, 博主本人也是一名学生, 也还有需要很多学习的东西. 写这篇文章是以本人所学内容为基础, 日后也会不断更新自己的学习记录, 我们一起努力进步, 变得优秀, 小小菜鸟, 也能有大大梦想, 关注我, 一起学习.

再次感谢你们的阅读, 你们的鼓励是我创作的最大动力!!!!!

相关推荐
wainyz5 分钟前
Java NIO操作
java·开发语言·nio
工业3D_大熊10 分钟前
【虚拟仿真】CEETRON SDK在船舶流体与结构仿真中的应用解读
java·python·科技·信息可视化·c#·制造·虚拟现实
喵叔哟13 分钟前
重构代码之用委托替代继承
开发语言·重构
lzb_kkk19 分钟前
【JavaEE】JUC的常见类
java·开发语言·java-ee
SEEONTIME19 分钟前
python-24-一篇文章彻底掌握Python HTTP库Requests
开发语言·python·http·http库requests
起名字真南38 分钟前
【OJ题解】C++实现字符串大数相乘:无BigInteger库的字符串乘积解决方案
开发语言·c++·leetcode
爬山算法43 分钟前
Maven(28)如何使用Maven进行依赖解析?
java·maven
tyler_download1 小时前
golang 实现比特币内核:实现基于椭圆曲线的数字签名和验证
开发语言·数据库·golang
小小小~1 小时前
qt5将程序打包并使用
开发语言·qt
hlsd#1 小时前
go mod 依赖管理
开发语言·后端·golang