JVM虚拟机(二)类加载器、双亲委派模型、类装载的执行过程

news2025/6/25 21:08:00

目录

    • 一、类加载器
      • 1.1 什么是类加载器?
      • 1.2 类加载器的分类
    • 二、双亲委派模型
      • 2.1 什么是双亲委派模型?
        • 1) 示例一:加载自己创建的类
        • 2)示例二:加载JDK原有的类
      • 2.2 JVM 为什么采用双亲委派模型?
    • 三、类装载的执行过程
      • 3.1 加载
      • 3.2 验证
      • 3.3 准备
      • 3.4 解析
      • 3.5 初始化
      • 3.6 使用
      • 3.7 卸载
      • 3.8 总结

一、类加载器

1.1 什么是类加载器?

首先,我们来看一下类加载器在 JVM 组成中的位置:

在这里插入图片描述

  • 类加载器:用于装载字节码文件(.class文件)。
  • 运行时数据区:用于分配存储空间。
  • 执行引擎:执行字节码文件或本地方法。
  • 垃圾回收器:用于对 JVM 中的垃圾内容进行回收。

当 Java 源码编译为 Class 文件之后,由类加载子系统将 .class 字节码文件装载到 运行时数据区。然后运行数据区去分布内存,然后再去执行就可以了。下面我们来看一下更加完整的回答:

  • 类加载器:JVM 只会运行二进制文件,类加载器 的作用就是将字节码文件加载到JVM中,从而让 Java 程序能够启动起来。

我们知道了类加载器之后,那么类加载器有哪些种类呢?

1.2 类加载器的分类

大家来看下面的图,类加载器一共分了四种,它们分别各司其职:

  1. 第一个是启动类加载器,或者叫引导类加载器(BootStrap ClassLoader)。用 C++ 语言编写,嵌套在 JVM 内部,主要用来加载 Java 的核心库。它会去加载 JAVA_HOME/jre/lib 路径下的所有 jar 包。

  2. 第二个是扩展类加载器(ExtClassLoader),它主要加载 JAVA_HOME/jre/lib/ext 路径下的所有 jar 包,其实就是扩展目录。如果用户自己的 jar 包也放在 JAVA_HOME/jre/lib/ext 目录下的话,也会自动使用扩展类加载器去加载里面的类。

  3. 第三个是应用类加载器(AppClassLoader),它负责加载环境变量 CLASSPATH 下面所有的类,是默认的类加载器。一般来说,Java 应用的类都是由它去完成加载。

    在 Java 的日常开发中,基本上都是由以上三种类加载器配合来去完成加载的,各自加载各自的类。

  4. 最后一个是自定义类加载器(CustomizeClassLoader),自定义类继承 ClassLoader,实现自定义类加载规则。这个在我们平时的开发中用的并不是很多。

以上就是全部的四种类加载器。一般面试官问到这里,都没有问完,还会问一下什么是双亲委派模型?


二、双亲委派模型

2.1 什么是双亲委派模型?

双亲委派模型:英文是 Parent Delegation Model,这里的 “双亲” 实际指的是父级。加载某一个类,先委托上一级的加载器进行加载,如果上级加载器也有上级,则会继续向上委托,如果该类委托上级没有被加载,子加载器再尝试加载该类。

1) 示例一:加载自己创建的类

举个例子,我们自己编了一个 Student 类:

  • 按照类加载器的分类,应该是在应用类加载器(AppClassLoader)中加载。
  • 双亲委派的意思就是先不进行加载,它要委托上级加载器进行加载,也就是扩展类加载器(ExtClassLoader)。
  • 而扩展类加载器也有上级,所以它会继续向上委托,就找到了启动类加载器(BootStrap ClassLoader)。
  • 但是我们会发现,Student 类并不在 JAVA_HOME/jre/lib 和 JAVA_HOME/jre/lib/ext 目录下,所以说这两个类加载器并不能加载这个类。
  • 这个时候才会由应用类加载器(AppClassLoader)去加载 Student 类。
2)示例二:加载JDK原有的类

java.lang.String 类是我们在 Java 开发过程中经常会用到的一个字符串类,假如我们在当前的代码中用到了 String 类:

  • 首先会来到应用类加载器(AppClassLoader),它会向上委托。
  • 但是扩展类加载器(ExtClassLoader)也有上级,它也会向上委托。
  • 然后启动类加载器(BootStrap ClassLoader)就会去 lib 目录中找有没有 String 类,找到后就会去加载 String 类,然后将加载好的 String 类返回给 AppClassLoader,让它直接去使用就可以了。

以上就是双亲委派模式,但是面试官还会问:为什么会采用双亲委派机制呢?


2.2 JVM 为什么采用双亲委派模型?

这里有两个原因:

  1. 通过双亲委派机制可以避免某一个类被重复加载,当父类已经加载后则无需重复加载,保证唯一性。
  2. 为了安全,保证类库 API 不会被修改。

举个例子:比如我们自己定义了一个 String 类,如下所示:

package java.lang;

public class String {
    public static void main(String[] args) {
        System.out.println("demo info");
    }
}

此时执行 main() 方法,就会出现异常:

  • 在类 java.lang.String 中找不到 main 方法。

在这里插入图片描述

为什么会报这个错误呢?主要原因还是因为双亲委派机制。

  • 由于是双亲委派的机制,自己定义的 java.lang.String 类在启动类加载器中并没有得到加载,而是加载了核心 jre 库中有相同名字的类文件,但该类中并没有 main() 方法。
  • 这样可以防止而已篡改核心API库。

三、类装载的执行过程

这里会比较难,因为类装载的步骤实在是太多了,大家不太能够记得住,所以建议先去理解。

类装载的执行过程:类从加载到虚拟机中开始,直到卸载为止,它的整个生命周期包括了:加载、验证、准备、解析、初始化、使用和卸载这 7个阶段。其中,验证、准备和解析这三个部分统称为连接(linking)。

看到这里可能大家有点懵,没关系,下面我们对每个阶段进行详细讲解。

3.1 加载

在这里插入图片描述

加载阶段,主要做了以下事情:

  • 通过类的全名,获取类的二进制数据流。
  • 解析类的二进制数据流为方法区内的数据结构(Java类模型),也就是把类的信息存入方法区。
  • 创建 java.lang.Class 类的实例,表示该类型。作为方法区这个类的各种数据的访问入口。

为了方便理解,我们来看一下下面这个图:

比如说现在有一个 Person 类,它被类加载器加载之后就会存入运行时数据区。在运行时数据区中,有两个区域进行存储,当然作用是不一样的:

  • 第一个是方法区/元空间(Metaspace),存储的是 Person 类的信息。比如:Person 类的构造函数、方法、字段等。它都是存储的类的结构。
  • 第二个是堆(Heap),在堆中会开辟一段空间去存储类的 Class 对象。这有什么作用呢?后期当我们去创建对象的时候,比如说创建了 “张三” 和 “李四” 两个对象:
    • 这两个对象都是基于 Person 的 Class 去创建的,所以每个对象的对象头都会指向 Person 的 Class 对象。
    • 但是 “张三” 和 “李四” 这些具体类中的数据(比如:方法、构造函数、字段)需要通过 方法区 才能获得。这个时候,Person 的 Class 对象就能找到方法区中 Person 类对应的数据结构,来创建这两个对象。

这个就是加载过程中所要做的事了,主要就是先把二进制数据流读入到运行时数据区,在元空间中存储类的信息,在堆中开辟一块空间来存储这个类的 Class 对象,方便后期创建对象时使用。

下面我们来看第二个阶段:

3.2 验证

在这里插入图片描述

验证阶段,属于连接的一部分,主要是验证类是否符合 JVM规范,进行安全性检查。检查内容分为四项:

  1. 文件格式验证

  2. 元数据验证

  3. 字节码验证

    前三项都属于 格式检查,如:文件格式是否错误、语法是否错误、字节码是否合规。

  4. 符号引用验证:Class 文件在其常量池中会通过字符串记录自己将要使用的其他类或者方法,检查它们是否存在。

    比如下面这个使用 javap 命令看到的字节码中对的常量池,方法在引用的时候,都会到常量池中查表翻译,找到对应的类或者方法去执行。所以这里验证的主要是这些方法或者类是否存在,如果不存在就会报类或者方法找不到(ClassNotFoundExcaption、MethodNotFoundException)。这也属于安全校验的一部分。

以上就是验证的部分,下面我们看一下第三阶段。

3.3 准备

在这里插入图片描述

准备阶段,也属于连接的一部分,主要是为类变量分配内存,并设置类变量初始值。那什么是类变量呢?类变量,也称静态变量,就是类中被 static 修饰的变量。

准备阶段分为三种情况:

  1. static 修饰的变量,分配空间的时候在当前的准备阶段完成(设置默认值),真正赋值的时候在初始化阶段才会完成。

    例如:下图中的 b 变量,在当前准备阶段,它只会赋一个默认值,由于 b 是 int 类型,那么默认值是 0。真正把 b 赋值为 10 的时候是在初始化阶段才会完成。

  2. static 修饰的变量是 final 的基本类型(int、long等),或者字符串常量,这个时候值已经是确定的,所以在准备阶段就完成了赋值的操作。

    例如:下图中的 c、d 变量,它们是被 static 和 final 同时修饰的基本类型,这种情况就会在准备阶段直接完成赋值。

  3. static 修饰的变量是 final 的引用类型(Object等),那么赋值也会在初始化阶段完成。

    例如:下图中的 obj 变量,它是被 static 和 final 同时修饰的 Object,这种情况并不会在当前准备阶段去赋值,而是和没有被 final 修饰的基本类型一样,也是在初始化阶段完成赋值。

以上就是准备阶段所需要完成的事了,下面我们再来看一下第四阶段。

3.4 解析

在这里插入图片描述

解析阶段,也属于连接的一部分,主要是把类中的符号引用转换为直接引用

什么是符号引用?什么是直接引用呢?
比如:方法中调用了其他方法,方法名可以理解为符号引用,而直接引用就是使用指针直接指向方法。我们还是使用之前 javap 解析出来的字节码为例:

在这里插入图片描述

左边是 main() 方法的执行指令,右边是常量池,执行的时候我们需要到常量池中查表翻译,这里就会找到当前某一个方法(Methodref 表示方法定义),#4 会引用 #24 和 # 25:

可以看到 #24 是一个 Class,#25 是 NameAndType,其中 #24 还引用了 #31,#25 还引用了 #32 和 #33。

其实当前的 #1、#2、……、#33 就是 符号引用,我们真正要去执行的就是根据符号引用找到对应的类,并且找到这个类中对应的方法。如果是直接找到了对应的类或方法的话,就是 直接引用

这个就是解析阶段所要完成的事了,下面我们看一下初始化阶段。

3.5 初始化

在这里插入图片描述

初始化阶段,主要是对类的静态变量,静态代码块执行初始化操作。这里面有两项要求:

  • 如果初始化一个类的时候,其父类尚未初始化,则优先初始化其父类。
  • 如果同时包含多个静态变量或静态代码块,则按照自上而下的顺序依次执行。

下面我们来演示一下,代码如下:

public class Application {

    public static void main(String[] args) {
        // 1.首次访问这个类的静态变量或静态方法时
        System.out.println(Animal.num);
        // 2.子类初始化,如果父类还没初始化,会引发父类先初始化
//        System.out.println(Cat.sex);
        // 3.子类访问父类静态变量,只触发父类初始化
//        System.out.println(Cat.num);
    }
}

class Animal {
    static int num = 55;
    static {
        System.out.println("Animal 静态代码块...");
    }
}

class Cat extends Animal {
    static boolean sex = false;
    static {
        System.out.printf("Cat 静态代码块...1");
    }

    static {
        System.out.printf("Cat 静态代码块...2");
    }
}

测试1: 如果是首次访问这个类的静态变量或静态方法时,就会去初始化。

我们直接 Animal 类的静态变量 num,看下会不会初始化,代码和执行结果如下:

System.out.println(Animal.num);

在这里插入图片描述

可以看到,Animal 中的静态代码块正常执行了,我们也拿到了静态变量。

测试2: 子类初始化,如果父类还没初始化,就会引发父类先初始化。

代码中 Cat 是 Animal 的子类,当我们调用了 Cat 的静态变量 sex 的时候,它会去检查当前父类 Animal 有没有初始化, 假如 Animal 没有被初始化,它就会先去初始化父类。代码和执行结果如下:

System.out.println(Cat.sex);

在这里插入图片描述

可以看到,先去初始化了父类 Animal 的静态代码块,然后才会执行 Cat 类的静态代码块,最后打印变量值 。

如果同时包含了多个静态代码块,比如 Cat 类,那么它会按照从上到下的顺序去执行。

测试3: 直接用子类去调用父类的静态变量,这时候只会触发父类的初始化,并不会初始化子类。代码和执行结果如下:

System.out.println(Cat.num);

在这里插入图片描述

可以看到,当子类调用父类的静态变量时,只初始化了父类的代码块。

以上就是初始化阶段所要完成的事了,下面我们再来看两个操作:使用、卸载。

3.6 使用

在这里插入图片描述

有两种情况代表我们使用了这个类:

  1. 当我们调用静态类成员信息(比如:静态字段、静态方法),就代表我们使用了这个类。
  2. 使用 new 关键字为这个类创建了对象实例,也代表我们使用了这个类。

3.7 卸载

在这里插入图片描述

当程序代码执行完毕之后,JVM 就开始销毁创建的 Class 对象了,这个时候就相当于把类删除卸载了。

到这里,就介绍完了类加载的执行过程,一共分为7个阶段,下面我们进行下总结:

3.8 总结

  1. 加载: 查找和导入 class 文件。
  2. 验证: 保证加载类的准确性。
  3. 准备: 为类变量分配内存并设置类变量初始值。
  4. 解析: 把类中的符号引用转换为直接引用。
  5. 初始化: 对类的静态变量,静态代码块执行初始化操作。
  6. 使用: JVM 开始从入口方法开始执行用户的程序代码。
  7. 卸载: 当用户程序代码执行完毕后,JVM 便开始销毁创建的 Class 对象。

整理完毕,完结撒花~🌻





参考地址:

1.新版Java面试专题视频教程,java八股文面试全套真题+深度详解(含大厂高频面试真题),https://www.bilibili.com/video/BV1yT411H7YK

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1584238.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

3. DAX 时间函数-- DATE 日期--一生二,二生三,三生万物

在数据分析过程中,经常需要从一个数据推到另外一个数据,日期数据也是如此,需要从一个日期推到另外一个相关的日期,或者从一群日期推到另外一个相关的日期/一群相关的日期。这一期说的就是日期之间彼此推衍的函数,会比之…

笔记83:二叉树前中后序遍历(迭代法 + 栈)

题目:. - 力扣(LeetCode) 注意:以下代码均为个人尝试编写,并非力扣题解,因此时间和空间复杂度可能并不是最优的,只是记录一下自己当时写这个题的时候的思路; 前序遍历: …

嵌入式ARM版本银河麒麟操作系统V10SP1安装OPenGauss数据库

前言: 官网提供了非常完整的openGauss安装步骤。 https://opengauss.org/zh/download/archive/列举一下个人的使用环境: 麒麟V10 rk3588工控板(ARM) openGauss-3.0.5(极简版)浏览一下官网,可以…

Unity Pro 2019 for Mac:专业级游戏引擎,助力创意无限延伸!

Unity Pro 2019是一款功能强大的游戏开发引擎,其特点主要体现在以下几个方面: 强大的渲染技术:Unity Pro 2019采用了新的渲染技术,包括脚本化渲染流水线,能够轻松自定义渲染管线,通过C#代码和材料材质&…

2024-04-10 作业

作业要求&#xff1a; 1> 思维导图 2> 作业1&#xff1a; 作业2&#xff1a; 运行代码&#xff1a; main.cpp #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QDebug> #include <QTimerEvent> #include <QTime> #include &…

零售EDI:Princess Auto EDI对接

Princess Auto 是一家加拿大零售连锁店&#xff0c;专门从事农场、工业、车库、液压和剩余物品的销售。 Princess Auto 总部位于马尼托巴省温尼伯&#xff0c;截至 2024 年 1 月在 10 个省份拥有并经营 55 家商店以及三个配送中心。各种商品均以其“Powerfist”和“Pro.Point”…

网络IO模型以及实际应用

网络IO模型 本文主要介绍了几种不同的网络IO模型&#xff0c;以及实际应用中使用到的Reactor模型等。 我们常说的网络IO模型&#xff0c;主要包含阻塞IO、非阻塞IO、多路复用IO、信号驱动IO、异步IO。 根据第一个阶段&#xff1a;是否需要阻塞&#xff0c;分为阻塞和非阻塞IO。…

华为ensp中PPP(点对点协议)中的CHAP认证 原理和配置命令

作者主页&#xff1a;点击&#xff01; ENSP专栏&#xff1a;点击&#xff01; 创作时间&#xff1a;2024年4月11日6点00分 PPP协议&#xff08;Point-to-Point Protocol&#xff09;是点到点协议&#xff0c;是一种常用的串行链路层协议&#xff0c;用于在两个节点之间建立点…

虚幻引擎架构自动化及蓝图编辑器高级开发进修班

课程名称&#xff1a;虚幻引擎架构自动化及蓝图编辑器高级开发进修班 课程介绍 大家好 我们即将推出一套课程 自动化系统开发。 自动化技术在项目开发的前中后期都大量运用。如何您是一家游戏公司&#xff0c;做的是网络游戏&#xff0c;是不是经常会遇到程序员打包加部署需…

MySQL 主从复制部署(8.0)

什么是主从数据库 主从数据库是一种数据库架构模式&#xff0c;通常用于提高数据库的性能、可用性和可伸缩性。 它包括两种类型的数据库服务器&#xff1a; 1&#xff09;主数据库&#xff08;Master&#xff09;&#xff1a;主数据库是读写数据的主要数据库服务器。所有写操…

Llama 3下月正式发布,继续开源!

4月10日&#xff0c;Techcrunch消息&#xff0c;Meta在本周伦敦举办的一场活动中确定&#xff0c;下个月将正式发布Llama 3并且继续开源。 Meta全球事务总裁Nick Clegg表示&#xff0c;我们希望在下个月&#xff0c;甚至更短的时间内&#xff0c;正式推出新一代基础模型Llama …

Builder 生成器

意图 将一个复杂对象的构建与它的表示分离&#xff0c;是的同样构建过程可以创建不同的表示。 结构 其中 Builder为创建一个Product对象的哥哥部件指定抽象接口ConcreteBuilder实现Builder的接口以构造和装配该产品的各个部件&#xff0c;定义并明确它所创建的表示&#xff…

【I/O】基于事件驱动的 I/O 模型---Reactor

Reactor 模型 BIO 到 I/O 多路复用 为每个连接都创建一个线程 假设我们现在有一个服务器&#xff0c;想要对接多个客户端&#xff0c;那么最简单的方法就是服务端为每个连接都创建一个线程&#xff0c;处理完业务逻辑后&#xff0c;随着连接关闭线程也要销毁&#xff0c;但是…

鸿蒙实战开发-如何实现标准化数据定义与描述的功能。

介绍 本示例主要使用ohos.data.uniformTypeDescriptor 展示了标准化数据定义与描述的功能&#xff0c;在新增预置媒体文件后&#xff0c;对媒体文件的utd标准类型获取、utd类型归属类型查询、获取文件对应的utd类型的默认图标、支持自定义数据类型等功能。 实现过程中还使用到…

贪心算法简介

目录 一、什么是贪心算法&#xff1f; 二、贪心算法的特点 三、贪心算法解决找零问题、最短路径问题、背包问题 1.找零问题 2.最短路径问题 3.背包问题 一、什么是贪心算法&#xff1f; 贪心算法就是希望通过局部最优来解决全局最优 基本步骤&#xff1a;1.将问题分为若…

高精度地图导航论文汇总

文章目录 2022基于高精度地图的智能车辆路径规划与跟踪控制研究[M] 2023一种无人驾驶融合决策方案的设计与实现[M] 2022 基于高精度地图的智能车辆路径规划与跟踪控制研究[M] 摘要&#xff1a; 随着计算机及通信技术的不断进步&#xff0c;汽车行业也得到了飞速的发展。汽车在…

云计算重要概念之:虚拟机、网卡、交换机、路由器、防火墙

一、虚拟机 (Virtual Machine, VM) 1.主流的虚拟化软件&#xff1a; 虚拟化软件通过在单个物理硬件上创建和管理多个虚拟环境&#xff08;虚拟机&#xff09;&#xff0c;实现资源的高效利用、灵活部署、隔离安全以及便捷管理&#xff0c;是构建云计算和现代化数据中心的核心…

VideoGPT:Video Generation using VQ-VAE and Transformers

1.introduction 对于视频展示&#xff0c;选择哪种模型比较好&#xff1f;基于似然->transformers自回归。在没有空间和时间溶于的降维潜在空间中进行自回归建模是否优于在所有空间和时间像素级别上的建模&#xff1f;选择前者&#xff1a;自然图像和视频包括了大量的空间和…

盲人独立出行的新里程:“盲人软件”赋能无障碍生活

作为一名资深记者&#xff0c;我始终致力于探索并分享那些以科技之力提升特殊群体生活质量的故事。最近&#xff0c;一款名为蝙蝠避障的盲人软件进入了我的视野&#xff0c;其强大的避障导航功能正悄然改变着视障人士的出行方式&#xff0c;赋予他们前所未有的独立生活能力。 …

windows安装charles抓包iphone

安装charles抓包iphone charles基础介绍windows安装 charles基础介绍 Charles 是在 PC 端常用的网络封包截取工具&#xff0c;在做移动开发时&#xff0c;我们为了调试与服务器端的网络通讯协议&#xff0c;常常需要截取网络封包来分析。除了在做移动开发中调试端口外&#xf…