JVM - 字节码文件详解

news2024/9/20 0:54:39

文章目录

目录

文章目录

1. 无关性基石

2. Class类文件结构

magic- 魔数

主副版本号

常量池

访问标志

类索引,父类索引与接口索引集合

字段

方法

属性

3. 类加载机制

类的生命周期

类加载过程

加载

连接

验证

准备

解析

初始化

4. 类加载器

类与类加载器

类加载器的分类

启动类加载器 

扩展类加载器

应用程序类加载器

双亲委派模型

打破双亲委派模型

打破双亲委派机制–自定义类加载器

打破双亲委派机制的第二种方法

打破双亲委派机制的第三种方法

总结


1. 无关性基石

Java在刚刚诞生之时曾经提出过一个非常著名的宣传口号 "一次编写, 到处运行"。它的底气就是Oracle公司和其他虚拟机发布商发布的许多虚拟机都可以运行在各种不同的硬件平台和操作系统上的虚拟机, 这些虚拟机都可以载入一种平台无关的字节码, 从而实现  "一次编写, 到处运行" 。

实现语言无关性的基础仍然是虚拟机和字节码存储格式。Java虚拟机不和包括Java在内的任何语言进行绑定, 它只与 "class文件" 这种特殊的二进制文件所关联。

2. Class类文件结构

Class文件是一组以8个字节为基础单位的二进制流, 各个数据项目严格按照顺序紧凑地排列在文件之中, 中间没有添加任何的分隔符,这使得整个Class文件中存储的内容几乎全是程序运行的必要数据,没有空隙存在。当遇到需要占用8个字节以上的数据项时,则会按照高位在前的方式分割成若干8个字节进行存储。

根据 《Java虚拟机规范》 的规定,Class文件格式采用一种类似于c语言结构体的伪结构体来存储数据, 这种伪结构体只存在两种数据类型: "无符号数" 和"表"。

  • 无符号数属于基本的数据类型, 使用u1,u2,u4,u8分别表示1,2,4,8个字节。
  • 表是由多个无符号数或者其他表为数据项构成的复合数据类型。为了便于区分, 所有的表都以 "_info"结尾。

Class文件格式

类型名称数量释义
u4magic1魔数
u2minor_version1次版本号
u2major_version1主版本号
u2constant_pool_count1常量池个数
cp_infoconstant_poolconstant_pool_count-1常量池
u2access_flags1访问标志
u2this.class1类索引
u2super.class1父类索引
u2interfaces_count1接口索引集合数量
u2interfacesinterfaces_count接口索引集合
u2fields-count1字段表集合个数
field_infofieldsfields-count字段表集合
u2methods_count1方法表集合个数
method_infomethodsmethods_count方法表集合
u2attributes_count1属性表集合个数
attribute_infoattributesattributes_count属性表集合

准备一段简单Java代码

public class TestClass1 {
    private int m;

    public int inc(){
        return m+1;
    }

    public static void main(String[] args) {

    }
}

使用jclss工具查看编译后的字节码文件

magic- 魔数

每个字节码文件的前四个字节被称之为魔数(Magic Number) ,它的唯一作用就是确定这个文件是否为一个能被虚拟机接受的Class文件。不仅是Class文件,很多文件格式标准中都有使用魔数来进行身份识别的习惯。

Class文件的魔数值为 0xCAFEBABE(咖啡宝贝)

主副版本号

紧接着魔数的4个字节存储的是Class文件的版本号,第5和第6个字节是次版本号,第7和第8是主版本号。

常量池

紧接着主次版本号之后的是常量池入口, 常量池可以比喻为Class文件里的资源仓库。

常量池中主要存放两大类常量: 字面量 和 符号引用, 字面量比较接近于java语言层面的常量概念, 入文本字符串, 被声明为final的常量值等。而符号引用则属于编译原理方面的概念,主要包括下面几类常量:

被模块导出或开放的包(Package)

  • 类和接口的全限定名
  • 字段的名称和描述符
  • 方法的名称和描述符
  • 方法句柄和方法类型
  • 动态调用点和动态常量

常量池中每一项常量都是一个表。

访问标志

在常量池结束之后,紧接着的2个字节代表访问标志,这个标志用来识别一些类或者接口层面的访问信息。

类索引,父类索引与接口索引集合

字段

当前类或接口声明的字段信息

方法

当前类或接口声明的方法信息

属性

类的属性,比如源码的文件名,内部类的列表。

3. 类加载机制

类的生命周期

一个类型从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期将会经历加载,验证,准备,解析,初始化,使用和卸载七个阶段,其中验证,准备,解析三部分统称为连接。

关于什么时候开始类加载过程的第一个阶段"加载" ,《Java虚拟机规范》 中并没有进行明确规定,这点根据虚拟机自行把控。但是对于初始化阶段,《Java虚拟机规范》则是严格规定了有且只有六种情况必须立刻对类进行“初始化”。

1) 遇到new, getstatic, putstatic或invokestatic 字节按指令时,下面是遇到的情况。

  • 使用new关键字实例化对象
  • 读取或者设置一个类型的静态字段(被 final修饰, 已在编译期把结果放入常量池的静态字段除外)的时候
  • 调用一个类型的静态方法的时候

2) 使用 Java.lang.reflect 包的方法对类型进行反射调用的时候, 如果类型没有进行过初始化,则需要先触发其初始化。

3) 当初始化类的时候,发现其父类还没有进行过初始化, 则需要先触发其父类的初始化。

4) 当虚拟机启动时, 用户需要指定一个要执行的类(包含main()方法的那个类), 虚拟机会先初始化这个主类。

5) 当使用jdk7 新加入的动态语言支持时

6) 当一个接口中定义了Jdk1.8 新加入的默认方法时,如果有这个接口的实现类发生了初始化,那该接口就要在其前进行初始化。

类加载过程

加载

1) 加载(Loading) 阶段第一步是类加载器根据类的全限定名通过不同的渠道以二进制流的方式获取字节码信息。

2)类加载器在加载完类之后,java虚拟机会将字节码中的信息保存到方法区中。

3)类加载完成类之后,java虚拟机会将字节码中的信息保存到方法区中。

生成一个instanceKlass对象,保存类的所有信息,里面还包含实现特定功能比如多态的信息。

4)同时,java虚拟机还会在堆中生成一份与方法区中数据类似的java.lang.Class对象。

作用是在java代码中去获取类的信息以及存储静态字段的数据(JDK8及之后)。

连接

验证

连接(Linking)阶段的第一个环节是验证,验证的主要目的是检测Java字节码文件是否遵守了《Java虚拟机规范》中的约束。这个阶段一般不需要程序员参与。

准备

准备阶段为静态变量(static)分配内存并设置初始值。

准备阶段只会给静态变量赋初始值,而每一种基本数据类型和引用数据类型都有其初始值。

数据类型初始值
int0
byte0
short0
long0L
char'\u0000'
double0.0
booleanfalse
引用数据类型null
解析

解析阶段主要是将常量池中的符号引用替换为直接引用。

直接引用不在使用编号,而是使用内存中地址进行访问具体的数据。

初始化

初始化阶段会执行静态代码块中的代码,并为静态变量赋值。

初始化阶段会执行字节码文件中clinit部分的字节码指令。

4. 类加载器

Java虚拟机的设计团队有意把类加载阶段中的 "通过一个类的全限定名来获取描述该类的二进制字节流" 这个动作放到Java虚拟机的外部去实现,以便让程序自己去决定如何去获取所需的类。实现这个动作的代码被称之为 "类加载器"。

类与类加载器

类加载器虽然只用于实现类的加载动作,但它在java程序中起到的作用却远远超过类加载阶段。对于任意一个类来说,都必须由加载它的类加载器和它本身共同确认其在java中的唯一性。简单来说就是完全相同的类经过不同的类加载器进行加载,那么这两个类就必定不相等。

类加载器的分类

类加载器分为两类,一类是Java代码中实现的,一类是Java虚拟机底层源码实现的。

启动类加载器 

启动类加载器(Bootstrap ClassLoader)是由Hotspot虚拟 机提供的、使用C++编写的类加载器。

默认加载Java安装目录/jre/lib下的类文件,比如rt.jar, tools.jar,resources.jar等。

扩展类加载器

扩展类加载器(Extension Class Loader)是JDK中提供的、 使用Java编写的类加载器。 默认加载Java安装目录/jre/lib/ext下的类文件。

应用程序类加载器

扩展类加载器和应用程序类加载器都是JDK中提供的、使用Java编写的类加载器。

它们的源码都位于sun.misc.Launcher中,是一个静态内部类。继承自URLClassLoader。具备通过目录 或者指定jar包将字节码文件加载到内存中。

双亲委派模型

站在Java虚拟机的角度来看,只有两种不同的类加载器: 一种是启动类加载器(Bootstrap ClassLoading),这个类加载器使用C++语言实现, 是虚拟机自身的一部分。另外一种就是其他所有的类加载器,这些类加载器都由Java语言实现,独立存在于虚拟机外部,并且全部继承自抽象类java.lang.ClassLoader

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

双亲委派模型解决什么问题?

  • 1) 重复的类: 如果一个类重复出现在三个类加载器的加载位置,那应该由谁来进行加载? 会不会出现重复?  启动类加载器加载,根据双亲委派模型,它的优先级是最高的,也不会出现重复
  • 2) 避免了系统类被覆盖的问题: 在自己项目中创建一个java.lang.String类, 会被加载吗?
  • 不能,会交由启动类加载器加载在rt.jar包中的String类
  • 3) 类加载器的关系
  • 应用类加载器的父类加载器是扩展 类加载器,扩展类加载器没有父类 加载器,但是会委派给启动类加载 器加载

双亲委派机制的作用

  • 1.保证类加载的安全性 通过双亲委派机制,让顶层的类加 载器去加载核心类,避免恶意代码 替换JDK中的核心类库,比如 java.lang.String,确保核心类 库的完整性和安全性。
  • 2.避免重复加载 双亲委派机制可以避免同一个类被 多次加载,上层的类加载器如果加 载过类,就会直接返回该类,避免 重复加载。

打破双亲委派模型

打破双亲委派机制的三种方式

打破双亲委派机制–自定义类加载器

先来分析ClassLoader的原理,ClassLoader中包含了4个核心方法。 双亲委派机制的核心代码就位于loadClass方法中。

 正确的去实现一个自定义类加载器的方式是重写findClass方法,这样不会破坏双亲委派机制。

案例

一个Tomcat程序中是可以运行多个Web应用的,如果这两个应用中出现了相同限定名的类,比如Servlet类, Tomcat要保证这两个类都能加载并且它们应该是不同的类。

 如果不打破双亲委派机制,当应用类加载器加载Web应用1中的MyServlet之后,Web应用2中相同限定名的 MyServlet类就无法被加载了。

Tomcat使用了自定义类加载器来实现应用之间类的隔离。 每一个应用会有一个独立的类加载器加载对应的类。

打破双亲委派机制的第二种方法

JDBC案例

1、启动类加载器加载DriverManager。

 2、在初始化DriverManager时,通过SPI机制加载jar包中的myql驱动。

 3、SPI中利用了线程上下文类加载器(应用程序类加载器)去加载类并创建对象。

这种由启动类加载器加载的类,委派应用程序类加载器去加载类的方式,打破了双亲委派机制。

打破双亲委派机制的第三种方法

OSGi模块化 

历史上,OSGi模块化框架。它存在同级之间的类加载器的委托加载。OSGi还使用类加载器实现了热部署的 功能。 热部署指的是在服务不停止的情况下,动态地更新字节码文件到内存中。

 


总结

以上就是这篇博客的主要内容了,大家多多理解,下一篇博客见!

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

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

相关文章

RT-Thread Nano版本在STM32G474上的快速移植

目录 概述 1 RT-Thread Nano 1.1 Nano版本介绍 1.2 RT-Thread Nano的特点 2 STM32Cube创建项目 2.1 配置参数 2.2 RT-Thread配置 3 RT-Thread适配MCU 3.1 预编译代码 3.2 解决编译错误 3.2.1 配置OS_TICK 3.2.2 使能finsh_config.h 3.3.3 移植FINSH 3.3 配置TICK …

《黑神话:悟空》专题合集MOD/修改器/壁纸/音乐/CG剧情

《黑神话:悟空》专题合集」 链接:https://pan.quark.cn/s/d67857f4e308 包含内容: 《黑神话:悟空》MOD合集 《黑神话:悟空》修改器(风灵月影) 《黑神话:悟空》壁纸合集 《黑神话&#xff1…

gpedit.msc本地组策略编辑器,结果发现竟然打不开了

本地组策略编辑器,结果发现竟然打不开了。 1建一个txt文件 List.txt ,粘贴如下内容(全部复制新粘贴哈): echo offpushd "%~dp0"dir /b C:\Windows\servicing\Packages\Microsoft-Windows-GroupPolicy-Clie…

<Rust>egui学习之部件(十一):如何在窗口中添加单选框radiobutton部件?

前言 本专栏是关于Rust的GUI库egui的部件讲解及应用实例分析,主要讲解egui的源代码、部件属性、如何应用。 环境配置 系统:windows 平台:visual studio code 语言:rust 库:egui、eframe 概述 本文是本专栏的第十一篇…

SpringBoot3+Vue3开发商店上货管理系统

系统介绍 上货管理系统是专门为各种类型商店打造的一款进货管理系统。针对整个商店进货流程,提供很多方便功能,帮助店家完成上货流程。比如上货清单管理功能、上货清单确认功能、供货商管理功能、商品管理功能等。 技术栈 后端:SpringBoot…

C++从入门到起飞之——priority_queue(优先级队列) 全方位剖析!

🌈个人主页:秋风起,再归来~🔥系列专栏:C从入门到起飞 🔖克心守己,律己则安 目录 1、priority_queue的介绍 2、priority_queue的使用 3、priority_queue的模拟实现 3.1、仿函数的介…

Redis-主从集群

主从架构 单节点Redis的并发能力是有上限的,要进一步提高Redis的并发能力,就需要搭建主从集群,实现读写分离。 主从数据同步原理 全量同步 主从第一次建立连接时,会执行全量同步,将master节点的所有数据都拷贝给sla…

Microsoft SC-100: Microsoft 网络安全架构师

SC-100认证介绍 Microsoft SC-100: Microsoft 网络安全架构师是微软网络安全方向的相关证书, 作为 Microsoft 网络安全架构师,你要将网络安全策略转化为保护组织的资产、业务和运营的功能。 你要设计、指导实现和维护遵循零信任原则和最佳做法的安全性解…

最新HTML5中的文件详解

第5章 HTML5中的文件 5.1选择文件 可以创建一个file类型的input,添加multiple属性为true,可以实现多个文件上传。 5.1.1 选择单个文件 1.功能描述 创建file类型input元素,页面中不再有文本框,而是 选择文件 按钮,右侧是上次文件的名称&a…

中秋将至,邮寄中秋礼品怎么才安心?

中秋节,是中华民族的传统佳节,承载着人们对团圆的期盼和对亲人的思念。在这个温馨的节日里,中秋礼品成为了许多人传递情感的方式。 在这个数字化的时代,虽然一通电话、一个视频就能拉近人与人之间的距离,但一份实实在在…

Autoware 定位之初始姿态输入(九)

0. 简介 这一讲按照《Autoware 技术代码解读(三)》梳理的顺序,我们来说一说Autoware中的初始化操作,这个软件包当中完成了ekf_localizer发送初始姿态的包。它接收来自GNSS/用户的粗略估计的初始姿态。将姿态传递给ndt_scan_match…

Linux 删除虚拟环境命令

查看当前都有哪些虚拟环境 # conda info --env 删除虚拟环境 py311 conda remove -n py311 --all

【Canvas与钟表】干支表盘

【成图】 【代码】 <!DOCTYPE html> <html lang"utf-8"> <meta http-equiv"Content-Type" content"text/html; charsetutf-8"/> <head><title>387.干支表盘</title><style type"text/css">…

热门宠物空气净化器测评,希喂、小米空气净化器真实测试对比

随着宠物在家庭中占据越来越重要的地位&#xff0c;如何保持家中空气的清新成为了许多铲屎官关注的重点。市面上的宠物空气净化器琳琅满目&#xff0c;其中希喂和小米两款产品备受关注。今天我们就从外观设计、功能性、滤芯效果、噪音控制和性价比五个方面&#xff0c;来为大家…

2024 9月最新PyCharm下载安装教程(详细步骤)附激活码!

PyCharm安装教程 一、软件简介 PyCharm是一款Python IDE&#xff0c;其带有一整套可以帮助用户在使用Python语言开发时提高其效率的工具&#xff0c;比如&#xff0c; 调试、语法高亮、Project管理、代码跳转、智能提示、自动完成、单元测试、版本控制等等。此外&#xff0c;…

设计模式-行为型模式-迭代器模式

1.迭代器模式的定义 迭代器模式提供一种对容器对象中的各个元素进行访问的方法&#xff0c;而不需要暴露该对象的内部细节&#xff1b; 在软件系统中&#xff0c;容器对象有两个职责&#xff1a;一是存储数据&#xff0c;二是遍历数据&#xff1b;从依赖性上看&#xff0c;前者…

C语言代码练习(第十六天)

今日练习&#xff1a; 40、编写程序&#xff0c;用 getchar 函数读入两个字符给c1和c2&#xff0c;然后分别用 putchar 函数和 printf 函数输出这两个字符。 41、输入4个整数&#xff0c;要求按由小到大的顺序输出。 42、有4个圆塔&#xff0c;圆心分别为&#xff08;2.2)、(-2…

笔记 13 : 彭老师课本第 8 章, UART ,概念,帧格式 , 工作原理,模块介绍,查看原理图 与 datasheet ,GPIO 组态 ,寄存器介绍

&#xff08;94&#xff09; 开始学习通信。通信谢意要考虑时钟同步&#xff0c;是否双工通信&#xff0c;并行或串行通信等等&#xff1a; 低速协议用 uart &#xff0c; iic &#xff0c; spi &#xff0c; 高速协议用 pci 。can 总线支持远距离传输&#xff0c;如门禁&a…

Cursor 使用 One API 配置 Anthropic Claude BaseURL 代理指南

背景 Cursor IDE 原生只支持配置 ChatGPT 的 API Base URL,无法直接使用 Anthropic Claude 的 API。 本指南将介绍如何通过One API来解决这个问题,实现在Cursor中使用Claude API。 前置条件 部署One API https://github.com/songquanpeng/one-api 获取Anthropic Claude A…

做运营,发布时间很重要

声明&#xff1a;此篇为 ai123.cn 原创文章&#xff0c;转载请标明出处链接&#xff1a;https://ai123.cn/#1 作为社交网络与媒体行业的内容运营&#xff0c;我常常被以下问题困扰&#xff1a;用户活跃时间难以预测、内容策划时间紧张、跨平台管理复杂、数据分析繁琐、创意枯竭…