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

目录

    • 一、类加载器
      • 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.rhkb.cn/news/304374.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

第14届java A组蓝桥杯做题记录

A题 特殊日期 package Java14省赛.Java研究生组;import java.time.Year; //特殊判断一下2月份,leaf 为true 1 import java.util.*;import 蓝桥杯.dfs_n皇后; public class 特殊日期 {static int sum(int d){int res 0;while(d > 0){res d % 10;d / 10;}return…

备战蓝桥杯Day40 - 第11届python组真题 - C跑步锻炼

一、题目描述 二、思路 1、使用datetime库中的方法可以很好的解决这个问题。 2、定义起始时间和结束时间,判断是否是周一或者是1号,结果res加上相应的里程数。 3、最后输出 res 即为本题答案。 三、代码实现 import datetimestart datetime.date(2…

NzN的数据结构--选择排序

接上文,本章我们来介绍选择排序。先三连后看才是好习惯~~~ 目录 一、基本思想 二、直接选择排序 三、堆排序 一、基本思想 每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待…

前端工程化理解 (2024 面试题)

最好介绍远古世界最好随性一点,不要太刻板 ,不然像背书 什么是前端工程化? - 知乎 前端工程化的历史 互联网初期,09 年以前,页面只需要展示一些列表、表格、文章内容以及简单图片即可,其目的是为了传送信…

SHOPFA:APP定制开发的哪种二开项目容易交付,哪些不可以接?

在商城系统开发领域,定制开发与二次开发(二开)是两种截然不同的项目类型。它们之间的主要差异体现在项目起点、灵活性、成本、时间以及风险等多个方面。 一、项目起点 商城定制开发通常是从零开始,根据客户的实际需求&#xff0c…

电介质材料(四)——复合电介质材料

本篇为西安交通大学本科课程《电气材料基础》的笔记。 本篇为这一单元的第四篇笔记,上一篇传送门。 复合电介质材料 是由多种成分共同组成,例如油纸复合绝缘、云母层压板、环氧浸渍玻璃纤维布等。即便是没有添加的材料,材料也会存在杂质和…

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

作者主页:点击! ENSP专栏:点击! 创作时间:2024年4月11日6点00分 PPP协议(Point-to-Point Protocol)是点到点协议,是一种常用的串行链路层协议,用于在两个节点之间建立点…

地表蒸散发遥感产品信息提取验证与融合

原文链接:地表蒸散发遥感产品信息提取验证与融合https://mp.weixin.qq.com/s?__bizMzUzNTczMDMxMg&mid2247600962&idx4&sncb995f8ac85c3c0759da82a15520c118&chksmfa820aa5cdf583b306988fbb5795c6370dab52a2fde5cfa5a8566dd7ba2864cb651c9230c6f3&…

企业级开源路由系统VyOS-构建和使用

介绍 VyOS是一个基于Linux的企业级路由器操作系统,被许多公司和个人用来驱动物理网络设备,如路由器和防火墙。它有一个统一的命令行界面来管理其所有的网络相关功能(和Juniper Junos操作很像)。VyOS使用Debian GNU/Linux作为其基…

xss基础

第一关&#xff1a; html部分标签可以解析js <script>alert (1)</script> 第二关&#xff1a; 可以看到value用双引号闭合了&#xff0c;使用上一关的payload没用&#xff0c;尝试一下闭合这个input 所以使用双引号和>闭合后再加入上一关的payload 11"…

Shotcut:免费且开源的优质视频剪辑工具

Shotcut&#xff1a;您的专业级免费开源视频编辑利器&#xff0c;助您轻松实现创意无限的剪辑梦想&#xff01;- 精选真开源&#xff0c;释放新价值。 概览 Shotcut&#xff0c;一款广受赞誉的免费、开源跨平台视频编辑软件&#xff0c;以其卓越的功能性和易用性赢得了全球用户…

打造智能健身时代:健身房会员管理系统解析

随着健康意识的提升和生活水平的提高&#xff0c;健身行业正迎来蓬勃发展的时代。健身房作为人们锻炼身体的重要场所&#xff0c;其会员管理系统的优劣直接影响到健身房的运营效率和服务质量。本文将探讨健身房会员管理系统的重要性以及如何打造智能化的健身时代。 1. 会员信息…

【今日刷题】LeetCode 199.二叉树的右视图(中等)

今日刷题&#xff1a;LeetCode 199.二叉树的右视图&#xff08;中等&#xff09; 题目描述&#xff1a; 给定一个二叉树的 根节点 root&#xff0c;想象自己站在它的右侧&#xff0c;按照从顶部到底部的顺序&#xff0c;返回从右侧所能看到的节点值。 示例 1: 输入: [1,2,3,…

Mysql底层原理四:B+树索引

B树索引&#xff08;索引的原理&#xff09; 1.前言 前边我们详细唠叨了InnoDB数据⻚的7个组成部分&#xff0c;知道了各个数据⻚可以组成⼀个双向链表&#xff0c;⽽每个数据⻚中的记录会按照主键值从⼩到⼤的顺序组成⼀个单向链 表&#xff0c;每个数据⻚都会为存储在它⾥边…

Python球球大作战

文章目录 写在前面球球大作战程序设计注意事项写在后面 写在前面 安装pygame的命令&#xff1a; pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pygame球球大作战 《球球大作战》是一款简单易上手、充满趣味性和竞技性的休闲手游。游戏的核心玩法可以用一句话概…

(React Hooks)前端八股文修炼Day9

一 对 React Hook 的理解&#xff0c;它的实现原理是什么 React Hooks是React 16.8版本中引入的一个特性&#xff0c;它允许你在不编写类组件的情况下&#xff0c;使用state以及其他的React特性。Hooks的出现主要是为了解决类组件的一些问题&#xff0c;如复杂组件难以理解、难…

qt-C++笔记之QLabel加载图片

qt-C笔记之QLabel加载图片 —— 2024-04-06 夜 code review! 文章目录 qt-C笔记之QLabel加载图片0.文件结构1.方法一&#xff1a;把图片放在项目路径下&#xff0c;在 .pro 文件中使用 DISTFILES添加图片文件1.1.运行1.2.qt_test.pro1.3.main.cpp 2.方法二&#xff1a;不在 .pr…

《深入Linux内核架构》第2章 进程管理和调度 (2)

目录 2.4 进程管理相关的系统调用 2.4.1 进程复制 2.4.2 内核线程 2.4.3 启动新程序 2.4.4 退出进程 本专栏文章将有70篇左右&#xff0c;欢迎关注&#xff0c;订阅后续文章。 2.4 进程管理相关的系统调用 2.4.1 进程复制 1. _do_fork函数 fork vfork clone都最终调用_…

计算机网络 Telnet远程访问交换机和Console终端连接交换机

一、实验要求和内容 1、配置交换机进入特权模式密文密码为“abcd两位班内学号”&#xff0c;远程登陆密码为“123456” 2、验证PC0通过远程登陆到交换机上&#xff0c;看是否可以进去特权模式 二、实验步骤 1、将一台还没配置的新交换机&#xff0c;利用console线连接设备的…

数学建模-最优包衣厚度终点判别法-二(K-Means聚类)

&#x1f49e;&#x1f49e; 前言 hello hello~ &#xff0c;这里是viperrrrrrr~&#x1f496;&#x1f496; &#xff0c;欢迎大家点赞&#x1f973;&#x1f973;关注&#x1f4a5;&#x1f4a5;收藏&#x1f339;&#x1f339;&#x1f339; &#x1f4a5;个人主页&#xff…