【茫茫架构路】1. Class File字节码文件解析

本文所有内容的JDK版本为 OpenJDK 11

JDK11 Class File官方说明。
Java解析字节码文件源码参考,以下为部分字节码解析源码展示。

public ClassFile(DataInputStream in) {try {//magic: 0xCAFEBABEthis.magic = ClassReader.readInt(in);System.out.println("magic: >>> " + this.magic);this.minorVersion = ClassReader.readUnsignedShort(in);System.out.println("minorVersion: >>> " + this.minorVersion);this.majorVersion = ClassReader.readUnsignedShort(in);System.out.println("majorVersion: >>> " + this.majorVersion);this.constantPoolCount = ClassReader.readUnsignedShort(in);System.out.println("constantPoolCount: >>> " + this.constantPoolCount);this.constantPool = new ConstantPoolInfo[this.constantPoolCount];for (int i = 1; i < this.constantPoolCount; i++) {ConstantPoolInfo cp = ConstantPoolFactory.getCp(in);this.constantPool[i] = cp;System.out.println("constantPool[" + i + "]: " + cp.getClass().getSimpleName() + " >>> " + JSONObject.toJSONString(cp));}this.accessFlags = ClassReader.readUnsignedShort(in);System.out.println("\naccessFlags: >>> " + this.accessFlags);//this class name index in constant poolthis.thisClass = ClassReader.readUnsignedShort(in);System.out.println("thisClass: >>> " + this.thisClass);//super class name index in constant poolthis.superClass = ClassReader.readUnsignedShort(in);System.out.println("superClass: >>> " + this.superClass);//interface countthis.interfaceCount = ClassReader.readUnsignedShort(in);System.out.println("interfaceCount: >>> " + this.interfaceCount);this.interfaces = new int[this.interfaceCount];for (int i = 0; i < this.interfaceCount; i++) {int f = ClassReader.readUnsignedShort(in);this.interfaces[i] = f;System.out.println("interfaces[" + i + "] >>> " + f);}//field countthis.fieldCount = ClassReader.readUnsignedShort(in);System.out.println("fieldCount: >>> " + this.interfaceCount);this.fields = new Field[this.fieldCount];for (int i = 0; i < this.fieldCount; i++) {Field field = new Field(in);this.fields[i] = field;System.out.println("fields[" + i + "] >>> " + JSONObject.toJSONString(field));}} catch (IOException e) {e.printStackTrace();}}

Java 虚拟机不与包括 Java 语言在内的任何程序语言绑定,它只与" Class 文件"这种特定的二进制文件格式所关联, Class 文件中包含了 Java 虚拟机指令集符号表以及若干其他辅助信息。基于安全方面的考虑,《 Java 虚拟机规范》中要求在 Class 文件必须应用许多强制性的语法和结构化约束。作为一个通用的与机器无关的执行平台,任何其他语言的实现者都可以将 Java 虚拟机作为他们语言的运行基础,以 Class 文件作为他们产品的交付媒介。
根据《Java虚拟机规范》的规定,Class文件格式采用一种类似于C语言结构体的伪结构来存储数据,这种伪结构只有两种数据类型:**无符号数****表**

  • 无符号数:以u1、u2、u4、u8来分别代表一个、两个、四个、八个字节的无符号数。无符号数可以用来描述数字、索引引用、数量值或者按照UTF8编码构成的字符串值。
  • 表:由多个或者其他表作为数据项构成的复合数据类型。有点类似于Java中所说的对象。
ClassFile {u4             magic;u2             minor_version;u2             major_version;u2             constant_pool_count;cp_info        constant_pool[constant_pool_count-1];u2             access_flags;u2             this_class;u2             super_class;u2             interfaces_count;u2             interfaces[interfaces_count];u2             fields_count;field_info     fields[fields_count];u2             methods_count;method_info    methods[methods_count];u2             attributes_count;attribute_info attributes[attributes_count];
}

Test.java是我们一个测试文件,生成的字节码文件见紧邻的代码块内容。

public class Test {private Integer variable;public static void main(String[] args) throws Exception {System.out.println("Hello World.");}
}
ca fe ba be 00 00 00 37  00 27 0a 00 06 00 18 09  |.......7.'......|
00 19 00 1a 08 00 1b 0a  00 1c 00 1d 07 00 1e 07  |................|
00 1f 01 00 08 76 61 72  69 61 62 6c 65 01 00 13  |.....variable...|
4c 6a 61 76 61 2f 6c 61  6e 67 2f 49 6e 74 65 67  |Ljava/lang/Integ|
65 72 3b 01 00 06 3c 69  6e 69 74 3e 01 00 03 28  |er;...<init>...(|
29 56 01 00 04 43 6f 64  65 01 00 0f 4c 69 6e 65  |)V...Code...Line|
4e 75 6d 62 65 72 54 61  62 6c 65 01 00 12 4c 6f  |NumberTable...Lo|
63 61 6c 56 61 72 69 61  62 6c 65 54 61 62 6c 65  |calVariableTable|
01 00 04 74 68 69 73 01  00 0e 4c 74 6f 64 6f 50  |...this...LtodoP|
6b 67 2f 54 65 73 74 3b  01 00 04 6d 61 69 6e 01  |kg/Test;...main.|
00 16 28 5b 4c 6a 61 76  61 2f 6c 61 6e 67 2f 53  |..([Ljava/lang/S|
74 72 69 6e 67 3b 29 56  01 00 04 61 72 67 73 01  |tring;)V...args.|
00 13 5b 4c 6a 61 76 61  2f 6c 61 6e 67 2f 53 74  |..[Ljava/lang/St|
72 69 6e 67 3b 01 00 0a  45 78 63 65 70 74 69 6f  |ring;...Exceptio|
6e 73 07 00 20 01 00 0a  53 6f 75 72 63 65 46 69  |ns.. ...SourceFi|
6c 65 01 00 09 54 65 73  74 2e 6a 61 76 61 0c 00  |le...Test.java..|
09 00 0a 07 00 21 0c 00  22 00 23 01 00 0c 48 65  |.....!..".#...He|
6c 6c 6f 20 57 6f 72 6c  64 2e 07 00 24 0c 00 25  |llo World...$..%|
00 26 01 00 0c 74 6f 64  6f 50 6b 67 2f 54 65 73  |.&...todoPkg/Tes|
74 01 00 10 6a 61 76 61  2f 6c 61 6e 67 2f 4f 62  |t...java/lang/Ob|
6a 65 63 74 01 00 13 6a  61 76 61 2f 6c 61 6e 67  |ject...java/lang|
2f 45 78 63 65 70 74 69  6f 6e 01 00 10 6a 61 76  |/Exception...jav|
61 2f 6c 61 6e 67 2f 53  79 73 74 65 6d 01 00 03  |a/lang/System...|
6f 75 74 01 00 15 4c 6a  61 76 61 2f 69 6f 2f 50  |out...Ljava/io/P|
72 69 6e 74 53 74 72 65  61 6d 3b 01 00 13 6a 61  |rintStream;...ja|
76 61 2f 69 6f 2f 50 72  69 6e 74 53 74 72 65 61  |va/io/PrintStrea|
6d 01 00 07 70 72 69 6e  74 6c 6e 01 00 15 28 4c  |m...println...(L|
6a 61 76 61 2f 6c 61 6e  67 2f 53 74 72 69 6e 67  |java/lang/String|
3b 29 56 00 21 00 05 00  06 00 00 00 01 00 02 00  |;)V.!...........|
07 00 08 00 00 00 02 00  01 00 09 00 0a 00 01 00  |................|
0b 00 00 00 2f 00 01 00  01 00 00 00 05 2a b7 00  |..../........*..|
01 b1 00 00 00 02 00 0c  00 00 00 06 00 01 00 00  |................|
00 08 00 0d 00 00 00 0c  00 01 00 00 00 05 00 0e  |................|
00 0f 00 00 00 09 00 10  00 11 00 02 00 0b 00 00  |................|
00 37 00 02 00 01 00 00  00 09 b2 00 02 12 03 b6  |.7..............|
00 04 b1 00 00 00 02 00  0c 00 00 00 0a 00 02 00  |................|
00 00 0c 00 08 00 0d 00  0d 00 00 00 0c 00 01 00  |................|
00 00 09 00 12 00 13 00  00 00 14 00 00 00 04 00  |................|
01 00 15 00 01 00 16 00  00 00 02 00 17           |.............|

magic

每个Class文件的头4个字节被称为魔数,即0xCAFEBABE,唯一作用就是确定这个文件是否为一个能被虚拟机接收的Class文件。类加载过程中的验证阶段就会包含此部分的验证。由于大小端差异,可能展示略有不同。

minor_version

魔数后面两个字节0000是次版本号。

major_version

次版本号后面两个字节0x0037是主版本号,十进制为55,即Java11对应的版本号。版本号的意义是:高版本的JDK能向下兼容以前版本的Class文件,但不能运行以后版本的Class文件。因为《Java虚拟机规范》在Class文件校验部分明确要求了即使文件格式并未发生任何变化,虚拟机也必须拒绝执行超过其版本号的Class文件。
image.png

constant_pool_count

接下来的两个字节代表常量池数量0x0027,十进制为39。与我们的习惯不同,计数是从1开始的而不是0,所以代表我们这个Class文件中有38个常量值,索引范围是1~38。之所以不从0开始,是因为0有特殊考虑,即不引用任何常量池时。

constant_pool

常量池。主要包含两大类常量:符号引用和字面量(又称直接引用)。

  • 符号引用:存放常量池索引
  • 字面量:常量。

常量池第一个字节为标志位0a 即十进制10,查表速得为CONSTANT_Methodref
image.png
CONSTANT_Methodref的结构为:

CONSTANT_Methodref_info {u1 tag;u2 class_index;u2 name_and_type_index;
}

往后读两个字节00 06即为class_index,该值为常量池的一个索引,表示指向常量池中#6元素。通过jclasslib工具可以知道这是一个CONSTANT_Class类型的常量池索引,最终指向的class name是一个java/lang/Object的字符串。
image.png
再往后读两个字节00 18即为name_and_type_index,十进制24,同样是常量池中的一个CONSTANT_NameAndType类型的索引,指向常量池中#24元素。通过jclasslib工具快速查看一下表示一个名为返回值为void的方法,即Java.lang.Object的初始化方法。
image.png
至此第一个常量池变量就解析完了,后面37个依次类推可依次解析完成。

access_flags

在常量池结束之后,紧接着的2个字节代表访问标志(access_flags),这个标志用于识别一些类或者接口层次的访问信息,包括:这个Class是类还是接口;是否定义为public类型;是否定义为abstract 类型;如果是类的话,是否被声明为final等等。
0x0021即为access_flag,即ACC_PUBLICACC_SUPER
image.png

this_class

this_class用于确定这个类的全限定名。0x0005表示指向常量池#5号索引。
image.png

super_class

super_class用于确定这个类的父类的全限定名,由于Java语言不允许多 重继承,所以父类索引只有一个,除了java.lang.Object之外,所有的Java类都有父类,因此除了 java.lang.Object外,所有Java类的父类索引都不为0。
0x0006表示指向常量池#6号索引。
image.png

interfaces_count

接口计数器(int erfaces _count ),表示索引表 的容量。0x0000表示指向接口数量为0。如果该类没有实现任何接口,则该计数器值为0,后面接口的索引表不再占用任何字节。

interfaces

接口索引表,每一个item指向常量池索引。由于此测试类没有接口,故而字节码文件缺失该部分内容。

fields_count

字段计数器,值为0x0001,说明这个类只有一个字段表数据 。

fields

用于描述接口或者类中声明的变量。包括类级变量以及实例级变量,但不包括在方法内部声明的局部变量。字段可以包括的修饰符有字段的作用域(public、private、protected修饰符)、是实例变量还是类变量(static修饰符)、可变性(final)、并发可见性(volatile修饰符,是否强制从主内存读写)、可否被序列化(transient修饰符)、字段数据类型(基本类型、对象、数组)、 字段名称。

field_info {u2             access_flags;u2             name_index;u2             descriptor_index;u2             attributes_count;attribute_info attributes[attributes_count];
}

image.png

access_flags

字段修饰符,0x0002表示ACC_PRIVATE

image.png

name_index

字段名称在常量池的索引,即0x0007

descriptor_index

字段和方法描述符在常量池的索引,即0x0008
描述符的作用是用来描述字段 的数据类型、方法的参数列表(包括数量、类型以及顺序)和返回值。根据描述符规则,基本数据类 型(byte、char、double、float、int、long、short、boolean)以及代表无返回值的void类型都用一个大写字符来表示,而对象类型则用字符L加对象的全限定名来表示
image.png
对于数组类型,每一维度将使用一个前置的“ [”字符来描述,如一个定义为“ java.lang.String[][]”类型 的二维数组将被记录成“ [[Ljava/lang/St ring;”,一个整型数组“int []”将被记录成“ [I”。
用描述符来描述方法时,按照先参数列表、后返回值的顺序描述,参数列表按照参数的严格顺序 放在一组小括号“()”之内。如方法void inc()的描述符为“()V”,方法java.lang.String toString()的描述符 为“()Ljava/lang/String;”,方法int indexOf(char[]source,int sourceOffset,int sourceCount,char[]target, int targetOffset,int targetCount,int fromIndex)的描述符为“([CII[CIII)I”。

attributes_count

同下attributes_count

attribute_info

同下attribute_info

methods_count

方法计数器,即0x0002,表示有两个方法。其实这个测试类中只有一个显示方法,但是大家别忘了还有一个无参构造函数方法<init>

methods

Class文件存储 格式中对方法的描述与对字段的描述采用了几乎完全一致的方式,方法表的结构如同字段表一样,依次包括访问标志(access_flags)、名称索引(name_index)、描述符索引(descriptor_index)、属性表集合(attributes)几项。

method_info {u2             access_flags;u2             name_index;u2             descriptor_index;u2             attributes_count;attribute_info attributes[attributes_count];
}

image.png

access_flags

因为volatile关键字和transient 关键字不能修饰方法,所以方法表的访问标志中没有了 ACC_VOLATILE标志和ACC_TRANSIENT标志。与之相对,synchronized、native、strictfp和abstract 关键字可以修饰方法,方法表的访问标志中也相应地增加了ACC_SYNCHRONIZED、 ACC_NATIVE、ACC_STRICTFP和ACC_ABSTRACT标志。
image.png
我们这个测试类中第一个方法字节码access_flags对应0x0001,即ACC_PUBLIC

name_index

方法名称在常量池的索引,即0x0009

descriptor_index

方法描述符在常量池的索引,相关说明同字段中的descriptor_index,即0x000a

attribute_info

同下attribute_info

attributes_count

同下attribute_info

attributes_count

属性计数器。

attribute_info

属性表(attribute_info)在前面的讲解之中已经出现过数次,Class文件、字段表、方法表都可以
携带自己的属性表集合,以描述某些场景专有的信息。其结构如下。

attribute_info {u2 attribute_name_index;u4 attribute_length;u1 info[attribute_length];
}

attribute_name_index

属性名称在常量池索引。attribute_name_index 处的constant_pool 条目必须是表示属性名称的CONSTANT_Utf8_info 结构。

attribute_length

属性长度。

info

为了能正确解析Class文件,《Java虚拟机规范》最初只预定义了9项所有Java虚拟机实现都应 当能识别的属性,而在最新的《Java虚拟机规范》的Java SE 11版本中,预定义属性已经增加到28项。具体如下。根据attribute_name_index可以加载到类名称,然后根据反射去找到对应的预定义属性类型进行解析。
image.png
字节码文件解析过程对后续**类加载**以及**字节码增强技术**都有很大裨益。

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

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

相关文章

高等数学啃书汇总重难点(五)定积分

最近都在忙着刷题&#xff0c;尤其是政治和英语也开始加量复习了&#xff0c;该系列断更了将近2个月~不过最近在刷题的时候又遇到一些瓶颈&#xff0c;因此回归基础来整理一下知史点~ 总的来说&#xff0c;虽然第五章也是重中之重&#xff0c;定理数量也很多&#xff0c;但&…

C语言每日一题(17)老人的数目

力扣 2678 老人的数目 给你一个下标从 0 开始的字符串 details 。details 中每个元素都是一位乘客的信息&#xff0c;信息用长度为 15 的字符串表示&#xff0c;表示方式如下&#xff1a; 前十个字符是乘客的手机号码。接下来的一个字符是乘客的性别。接下来两个字符是乘客的…

JMeter添加插件

一、前言 ​ 在我们的工作中&#xff0c;我们可以利用一些插件来帮助我们更好的进行性能测试。今天我们来介绍下Jmeter怎么添加插件&#xff1f; 二、插件管理器 ​ 首先我们需要下载插件管理器jar包 下载地址&#xff1a;Install :: JMeter-Plugins.org 然后我们将下载下来…

MYSQL(事务+锁+MVCC+SQL执行流程)理解

一)事务的特性: 一致性:主要是在数据层面来说&#xff0c;不能说执行扣减库存的操作的时候用户订单数据却没有生成 原子性:主要是在操作层面来说&#xff0c;要么操作完成&#xff0c;要么操作全部回滚&#xff1b; 隔离性:是自己的事务操作自己的数据&#xff0c;不会受到到其…

Centos 7 Zabbix配置安装

前言 Zabbix是一款开源的网络监控和管理软件&#xff0c;具有高度的可扩展性和灵活性。它可以监控各种网络设备、服务器、虚拟机以及应用程序等&#xff0c;收集并分析性能指标&#xff0c;并发送警报和报告。Zabbix具有以下特点&#xff1a; 1. 支持多种监控方式&#xff1a;可…

向量检索库Milvus架构及数据处理流程

文章目录 背景milvus想做的事milvus之前——向量检索的一些基础近似算法欧式距离余弦距离 常见向量索引1&#xff09; FLAT2&#xff09; Hash based3&#xff09; Tree based4&#xff09; 基于聚类的倒排5&#xff09; NSW&#xff08;Navigable Small World&#xff09;图 向…

mysql—面试50题—1

注&#xff1a;面试50题将分为5个部分&#xff0c;每部分10题 一、查询数据 学生表 Student create table Student(SId varchar(10),Sname varchar(10),Sage datetime,Ssex varchar(10)); insert into Student values(01 , 赵雷 , 1990-01-01 , 男); insert into Student …

微信小程序之会议OA首页数据交互,会议状态,会议人数转换,会议室交互,WXS的使用

前言&#xff1a; 本篇博客使用结合了SpringMVC&#xff0c;mybatis&#xff0c;maven&#xff0c;小程序&#xff0c;如果不熟悉使用可以翻看我之前的博客&#xff0c;以便大家可以更好的学习&#xff01;&#xff01;&#xff01; 一&#xff0c;会议OA首页数据的后台交互 这…

【SwiftUI模块】0060、SwiftUI基于Firebase搭建一个类似InstagramApp 2/7部分-搭建TabBar

SwiftUI模块系列 - 已更新60篇 SwiftUI项目 - 已更新5个项目 往期Demo源码下载 技术:SwiftUI、SwiftUI4.0、Instagram、Firebase 运行环境: SwiftUI4.0 Xcode14 MacOS12.6 iPhone Simulator iPhone 14 Pro Max SwiftUI基于Firebase搭建一个类似InstagramApp 2/7部分-搭建Tab…

机器学习笔记 - 特斯拉的占用网络简述

一、简述 ​ 2022 年,特斯拉宣布即将在其车辆中发布全新算法。该算法被称为occupancy networks,它应该是对Tesla 的HydraNet 的改进。 自动驾驶汽车行业在技术上分为两类:基于视觉的系统和基于激光雷达的系统。后者使用激光传感器来确定物体的存在和距离,而视觉系统…

算法通关村第十一关青铜挑战——移位运算详解

大家好&#xff0c;我是怒码少年小码。 计算机到底是怎么处理数字的&#xff1f; 数字在计算机中的表示 机器数 一个数在计算机中的二进制表示形式&#xff0c;叫做这个数的机器数。 机器数是带符号的&#xff0c;在计算机用一个数的最高位存放符号&#xff0c;正数为0&am…

【Java集合类面试八】、 介绍一下HashMap底层的实现原理

文章底部有个人公众号&#xff1a;热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享&#xff1f; 踩过的坑没必要让别人在再踩&#xff0c;自己复盘也能加深记忆。利己利人、所谓双赢。 面试官&#xff1a; 介绍一下HashMap底层的…

数据安全与PostgreSQL:最佳保护策略

在当今数字化时代&#xff0c;数据安全成为了企业不可或缺的一环。特别是对于使用数据库管理系统&#xff08;DBMS&#xff09;的组织来说&#xff0c;确保数据的完整性、保密性和可用性至关重要。在众多DBMS中&#xff0c;PostgreSQL作为一个强大而灵活的开源数据库系统&#…

MySQL中的表操作,配置文件,储存引擎,数据类型

MySQL中的表操作 1 查库&#xff08;已密码登陆mysql&#xff09; show databases; 2 添加库 create database t1; 3 表操作 1选定操作库 use t1 2在库里添加表格式 create table t1(id int, name varchar(32), gender varchar(32),age int); 3往表里添加具体元素 insert…

智慧燃气巡检管理系统

我们知道燃气设施的巡检、巡查是运维工作中一项重要的基础工作&#xff0c;而巡检人员主要靠手动记录&#xff0c;回到公司后还得再进行录入归档、导入照片&#xff0c;然后打印装订等&#xff0c;涉及工作量也是不小的&#xff1b;还有人员更替&#xff0c;易造成人员对燃气设…

【GWO-KELM预测】基于灰狼算法优化核极限学习机回归预测研究(matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

Dapper中使用字符串作为动态参数查询时,结果不是预期的问题

1、如下图&#xff0c;c.industryId作为string类型当作参数传递&#xff0c;解析时会加单引号&#xff0c;即&#xff1a;”c.industryId“&#xff0c; 生成的查询语句就会变成 -- 这里把c.IndustryGroup 当成实际的值所以会查询不出数据 select b.Name,COUNT(c.Id) Num …

数学建模——最大流问题(配合例子说明)

目录 一、最大流有关的概念 例1 1、容量网络的定义 2、符号设置 3、建立模型 3.1 每条边的容量限制 3.2 平衡条件 3.3 网络的总流量 4、网络最大流数学模型 5、计算 二、最小费用流 例2 【符号说明】 【建立模型】 &#xff08;1&#xff09;各条边的流量限制 &a…

Java赋值运算符(=)

赋值运算符是指为变量或常量指定数值的符号。赋值运算符的符号为“”&#xff0c;它是双目运算符&#xff0c;左边的操作数必须是变量&#xff0c;不能是常量或表达式。 其语法格式如下所示&#xff1a; 变量名称表达式内容 在 Java 语言中&#xff0c;“变量名称”和“表达式…