【JVM篇】类加载过程详解

news2024/10/5 20:11:22

目录

1、类加载过程概述

2、加载

3、连接

3.1 验证

3.1.1 文件格式验证

3.1.2 元数据验证

3.1.3 字节码验证

3.1.4 符号引用验证

3.2 准备

3.3 解析

4、初始化


 

1、类加载过程概述

想必大家一般在网上看类加载过程的资料时,通常资料只会将类加载过程概括成以下几点:

  1. 加载
  2. 连接(验证、准备、解析)
  3. 初始化

e7e6a7ab891d4980b71888b20390de0b.png

不可否认类加载的过程确实如上,但是大家知道里面的具体细节是怎么样的嘛?别急,本文接下来将会对以上过程一一进行详细的说明。

以上几个阶段是按顺序依次“开始”的。注意!这里说的是“按顺序开始”,而不是“依次、串行运行”。比如:可能加载的过程中,连接阶段已经开始,二者交叉运行。

2、加载

加载”阶段是整个“类加载”过程中的第一阶段(大家注意区分二者),在类加载阶段,Java虚拟机主要完成以下三件事情:

  1. 通过类的全限定名来获取该类的二进制字节流。
  2. 将这个字节流所代表的的静态存储结构转换成方法区运行时的数据结构。
  3. 在堆内存中生成这个类的java.lang.Class对象(方法区中的klass类是C++的一个类,里面保存了java类的常量池、方法、属性等信息,但是我们无法直接访问,因此需要创建一个Class对象作为方法区数据访问的入口)。

大家可以发现,加载过程中“获取”类的二进制字节流,我并没有强调是从哪里获取,《Java虚拟机规范》中也并没有要求从哪获取,如何获取。正是因为没有要求,这也为Java后来加载类玩出各式各样的花样提供了基础。比如获取类二进制字节流的途径可以随便举以下例子:

  • 从ZIP压缩包中获取。
  • 从网络中获取(典型应用是Web Applet)。
  • 运行时动态生成(动态代理技术)。
  • 从数据库中读取
  • 从加密文件中读取。
  • .............................(还有好多好多方式,我们就不一一举例了)

3、连接

连接我们通常分为三个过程:验证、准备、解析

3.1 验证

验证是加载阶段的第一个阶段,通常是保证class文件的字节流中包含的信息是有效的,符合《Java虚拟机规范》的全部约束要求,确保这些信息运行后不会产生危害。

其中,验证阶段主要是对以下四个方面进行验证:①文件格式验证。②元数据验证。③字节码验证。④符号引用验证。

3.1.1 文件格式验证

该阶段主要是为了验证字节流是否符合class文件格式的规范(因为文件格式是可以手动更改的,比如我们随便写了个txt文本,然后将文件后缀更改成 “.class” 结尾,如果不加以验证的话,肯定会出大问题的),并且能被当前版本的虚拟机运行。比如对以下几点进行验证:

  • 验证魔数(class文件的前四个字节)是否是0XCAFEBABE。
  • 主、次版本号是否在虚拟机能处理的范围内,《Java虚拟机规范》要求虚拟机只能向下兼容,不允许向上兼容,即虚拟机只能运行不大于自己当前版本的class文件。
  • 常量池中的常量是否存在不支持的常量类型(通过检查tag标志)。
  • Class文件是否存在部分内容的缺失。
  • ..........................................

该阶段作用是保证输入的字节流能够正确被解析并且加载于方法区内。该阶段的验证是基于二进制字节流进行的,只有通过该阶段,二进制字节流才能被加载进方法区,因此后面的几个阶段是基于方法区的数据结构进行的。

3.1.2 元数据验证

这一阶段主要是对字节码描述的信息进行语义分析,主要是保证字节码描述的信息符合《Java语言规范》的要求,比如:

  • 这个类是否有父类(除了Object类以外所有类应该都有父类)。
  • 这个类是否继承了不允许继承的类(比如final关键字修饰的类)
  • 这个类如果不是抽象类,是否实现了父类或接口中未实现的方法
  • ..........................................

3.1.3 字节码验证

这一阶段主要是通过数据流分析和控制流分析,确定程序语义是合法的。该阶段对方法体进行校验分析,保证该方法运行时候不会做出危害虚拟机的不安全行为。验证点例如:

  • 保证类型转换是有效的
  • 保证跳转指令不会跳转到方法体以外的字节码指令上
  • ..........................................

3.1.4 符号引用验证

这一阶段的校验行为发生在“符号引用”转为“直接引用”的时候,这个转化实际上发生在“连接”的第三阶段“符号解析阶段”。符号引用验证实际上是对类本身之外的各种类信息进行验证。验证点例如:

  • 符号引用中的类、字段、方法是否可访问(通过访问符:public,protected,private,package等)
  • 能否通过类的全限定名找到对应的类。
  • ..........................................

3.2 准备

准备阶段实际上是对类中定义的“类变量”,即静态变量(被static关键字修饰的变量)分配内存并设置初始值的阶段。

对于内存的分配,从概念上来说,JDK7以前是在方法区中给static变量分配内存,JDK8以后是在堆中。

准备阶段的主要工作上述已经描述了,这里还需要特别强调:一般情况下,这一阶段仅为类变量分配内存和初始化默认值。因此!实例变量(没被static关键字修饰的变量)是不会在该阶段分配内存和初始化默认值的

这里分配内存大家肯定都能理解,但是大家可能会有个疑惑:“什么叫初始化默认值?默认值是什么?”

默认值实际上就是我们在定义一个变量时,不设置值时,Java对于这种没设置值的变量赋予了一个“初始值”。这里我们给出基本类型的默认值(也可以叫初始化值、零值):

boolean           false
char              '/uoooo'(null)
byte              (byte)0
short             (short)0
int               0
long              0L
float             0.0f
double            0.0d
1、int类型定义的数组       默认是0
2、String类型定义的数组    默认值是null
3、char类型定义的数组      默认值是0对应的字符
4、double类型定义的数组    默认值是0.0
5、float类型定义的数组     默认值是0.0

比如我们以下代码,对于cug这个int类型的类变量,在这个阶段过后的值就是0,而不是2023:

public static int cug = 2023;

当然了,大家再看看我们上面说的那句话提到了“一般情况”,言外之意就是还有“特殊情况”,那么特殊情况是什么呢?那就是被“final”关键字修饰的类变量。由于被“final”关键字修饰的变量都是常量,是不可变的。对于这种类变量会在字段属性表中存在“ConstantValue”属性,那么此时在准备阶段便不是按零值来初始化,而是设置成“ConstantValue”属性所指定的值,例如以下代码在这个阶段后的值是2023,即我们指定的值,而不是零值。

public static final int cug = 2023;

3.3 解析

解析阶段实际上就是将符号引用转为直接引用的过程。

这个阶段一般来说比较复杂,本文不做过多描述,大家有兴趣的可以去看看周志明老师的《深入理解Java虚拟机》,里面讲的非常细节!!!强力推荐!!!

4、初始化

初始化阶段是类加载过程的最后一个阶段。在前面的准备过程中,我们已经为“一般情况”的类变量(即被static关键字修饰且不被final关键字修饰的变量)进行了内存的分配和设置零值。那么这个阶段大家肯定就能猜到要做什么了:“根据我们程序写好的代码进行其他变量的内存分配、设置我们指定的值”。

初始化阶段实际上就是执行类构造器<clinit>方法的过程。这里的<clinit>方法并不是我们在代码中直接编写的方法,而是Javac编译器自动生成的产物。

<clinit>方法主要是编译器自动收集类中所有类变量的赋值操作和静态代码块(static{ })中的语句合并而成,合并后的先后顺序是按我们程序中编写的先后顺序来决定。

对于<clinit>方法,有以下几个需要注意的地方:

  • 对于一个类来说,Java虚拟机会保证调用该类的<clinit>方法之前,其父类的<clinit>方法已经被调用。(因此可以知道Object类的<clinit>方法肯定是第一个被调用的,因为它是所有类的父类)。
  • <clinit>方法并不是必须生成的,如果一个类中没有静态代码块和类变量的赋值操作,那么不会生成<clinit>方法。
  • 接口虽然没有静态代码块,但可能会有变量的初始化赋值操作,因此也会生成对应的<clinit>方法。但是接口和类的<clinit>方法不同的是:接口不要求在执行<clinit>方法前,其父接口的<clinit>方法已经执行,仅仅是用到了父接口的变量才会执行父接口的<clinit>方法。
  • 为了保证<clinit>方法在多线程环境下的线程安全和仅被执行一次,因此<clinit>方法会被加锁进行同步。

 

 

 

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

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

相关文章

OMG--DDS(Data Distribution Service)

OMG--DDS&#xff08;Data Distribution Service&#xff09; 1 介绍1.1 概述1.2 OMG 涉及的规范 2 内容概述介绍目标 Data-Centric Publish-Subscribe (DCPS) 以数据为中心的发布-订阅概要Platform Independent Model (PIM) 平台独立模型格式和约定概念图总体概念模型PIM 描述…

ChatGPT工作提效之数据可视化大屏组件Echarts的实战方案(大数据量加载、伪3D饼图、地图各省cp中心坐标属性、map3D材质)

ChatGPT工作提效系列文章目录 ChatGPT工作提效之初探路径独孤九剑遇强则强ChatGPT工作提效之在程序开发中的巧劲和指令(创建MySQL语句、PHP语句、Javascript用法、python的交互)ChatGPT工作提效之生成开发需求和报价单并转为Excel格式ChatGPT工作提效之小鹅通二次开发批量API对…

【电路】电路与电子技术基础 课堂笔记 第7章 晶体管放大电路

7.1 放大的概念 7.1.1 放大电路基础 放大电路可以将电信号不失真地进行放大&#xff0c;而且是幅度放大&#xff1b; 本质上&#xff0c;放大是对能量进行控制和转换&#xff0c; 由一个能量较小的输入信号控制直流电源&#xff0c; 将直流电源的能量转换成与输入信号频率…

yolov8量化部署(基于openvino和tensorrt)

yolov8 openvino量化部署 环境配置&#xff1a; pip install ultralytics && pip install openvino-dev将pytorch模型转为openvino模型: from ultralytics import YOLO# Load a model model YOLO("./yolov8n.pt") # load an official model# Export the…

S7-200 PLC编程软件介绍

更多关于西门子S7-200PLC内容请查看&#xff1a;西门子200系列PLC学习课程大纲(课程筹备中) 西门子200PLC编程软件采用的是STEP 7-Micro/WIN 软件。它可以进行编写程序&#xff0c;PLC程序下载与上传&#xff0c;编程向导&#xff0c;程序编译&#xff0c;PLC程序监控等等功能…

软件工程开发文档写作教程(12)—概要设计书的编制目标

本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl本文参考资料&#xff1a;电子工业出版社《软件文档写作教程》 马平&#xff0c;黄冬梅编著 概要设计书概述 《概要设计说明书》又称为《系统设计说明书》&#xff0c;编制的目的是说明…

Cracking C++(10): 基本的输入输出流

文章目录 1. 目的2. I/O Streams 输入/输出流3. Stream Operators 流操作符4. 禁止使用 std::endl5. 缓冲区&#xff1a;直观理解6. References 1. 目的 查看 hackingcpp 上的 Input & Output (Basics) 教程后的笔记和拓展内容。 2. I/O Streams 输入/输出流 使用 C 的标…

chatgpt赋能python:合并多个文件——Python的终极解决方案

合并多个文件——Python的终极解决方案 Python是一种高级编程语言&#xff0c;其简单明了的语法和丰富的库使其成为开发者的首选语言之一。在日常编码中&#xff0c;我们有时候需要将多个文件合并成一个文件以便于处理。这篇文章将详细介绍Python如何进行多个文件的合并。 什…

高数下——查漏补缺

期末复习 一、向量与空间几何 二、多元函数与重极限 2.1定义域 2.2 二元函数重极限

【实战】体验SadTalker

论文http://openaccess.thecvf.com//content/CVPR2023/papers/Zhang_SadTalker_Learning_Realistic_3D_Motion_Coefficients_for_Stylized_Audio-Driven_Single_CVPR_2023_paper.pdf github GitHub - OpenTalker/SadTalker: [CVPR 2023] SadTalker&#xff1a;Learning Realist…

chatgpt赋能python:Python如何合并单元格-实用技巧教程

Python如何合并单元格 - 实用技巧教程 单元格合并是Excel文件和Word文档等办公软件中很常见的功能&#xff0c;而使用Python对数据进行处理时&#xff0c;也可能需要实现合并单元格的操作。本文将为大家介绍Python中实现合并单元格的方法&#xff0c;并给出代码示例。 什么是…

HTML段落标签

HTML段落标签 段落标签 显示特点&#xff1a; 代码&#xff1a; 显示效果&#xff1a; 总结&#xff1a; 答案&#xff1a;

Java性能权威指南-总结10

Java性能权威指南-总结10 垃圾收集算法理解G1垃圾收集器 垃圾收集算法 理解G1垃圾收集器 G1垃圾收集器是一种工作在堆内不同分区上的并发收集器。分区(region)既可以归属于老年代&#xff0c;也可以归属于新生代(默认情况下&#xff0c;一个堆被划分成2048个分区),同一个代的…

chatgpt赋能python:如何取消Python中的科学计数法

如何取消Python中的科学计数法 Python是一种面向对象、解释型的高级编程语言。由于它在数值计算、科学计算以及数据分析等领域的强大功能&#xff0c;Python语言已经成为科学计算和机器学习领域中最常用的语言之一。 然而&#xff0c;当我们进行大量运算时&#xff0c;Python…

[SpringBoot]Spring Security框架

目录 关于Spring Security框架 Spring Security框架的依赖项 Spring Security框架的典型特征 关于Spring Security的配置 关于默认的登录页 关于请求的授权访问&#xff08;访问控制&#xff09; 使用自定义的账号登录 使用数据库中的账号登录 关于密码编码器 使用BCry…

chatgpt赋能python:Python和Java:如何结合使用以提高开发效率

Python和Java&#xff1a;如何结合使用以提高开发效率 Python和Java是两种非常流行的编程语言&#xff0c;它们各自有自己的优势和用途。但有时候&#xff0c;我们需要将它们结合起来使用&#xff0c;以便更好地完成某些任务。在本文中&#xff0c;我们将介绍如何通过使用Java…

2.17 内存映射(1) 2.18内存映射(2)

2.17 内存映射&#xff08;1&#xff09; 内存映射 内存映射相关系统调用 void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);- 功能&#xff1a;将一个文件或者设备的数据映射到内存中- 参数&#xff1a;- void *addr: NULL, 由内核指定- le…

SpringSecurity学习 -- 1 springSecurity

目录 简介&#xff1a; token和jwt的区别 1.快速入门 1.自定义用户名和密码。 自定义重定向。 2.设置权限管理 3.从路径中获取用户信息 2.SpringSecurity核心组件 SecurityContext : authentication对象的容器。 SecurityContextHolder : Authentication: ​编辑 U…

Scala概述及变量和常量的重点

1.什么是scala Scala是一种多范式的编程语言&#xff08;多范式&#xff1a;多种编程方法的意思。有面向过程、面向对象、泛型、函数式四种程序设计方法&#xff09;&#xff0c;其设计的初衷是要集成面向对象编程和函数式编程的各种特性。Scala运行于Java平台&#xff08;Jav…

python语法-面向对象数据分析案例(每日销售额柱状图数据可视化)

面向对象数据分析案例&#xff08;每日销售额柱状图数据可视化&#xff09; 使用工具&#xff1a; Pycharm、面向对象、json模块、pyecharts模块等 实现步骤&#xff1a; 读取数据–封装数据对象–计算数据对象–pyechars绘图 &#xff08;项目数据见文章末参考内容&#xff09…