如何将字符串变量起名为class_「编译引擎」学习阅读Class文件结构(16进制版)-上...

我们来学习阅读一下16进制版的Class文件。

1.源头:Class规范

每一个Java版本发布时,会公布两份规范文档(以Java8为例):

Java语言规范:《The Java Language Specification, Java SE 8 Edition》

Java虚拟机规范:《The Java Virtual Machine Specification, Java SE 8 Edition》

Java虚拟机规范中有很大篇幅就是描述Class文件格式的规格,它也是帮助我们学习、理解Class文件最好的参考资料。

974374e1b0e86e29bdc80c4e960135bd.png

2.验证:演练代码

为了更好地覆盖JVM规范中描述的Class文件格式主脉络,笔者设计了如下示例代码:

c255d5effbc67ec220f71bb4a78fe00e.png

这段代码体现了5种Java的语法特征:

类:Demo2

成员变量:私有成员变量filed1,int类型,初始值为1

方法:公有方法hello,无参数无返回值

局部变量:局部变量i,int类型,初始值为1

语句:打印语句println,打印内容为hello world

使用javac编译上述代码,我们会得到Demo2.class文件,用16进制编辑器打开如下:

47495d698b15a74fd0b5f2c5f5512b0d.png

为了更清晰的查看,我们将上述数据复制到Excel表格中,着色

3a1a99084bcafe7ad3e72023563ca5d7.png

3.合法性检查依据:魔数、版本号

首先,通过阅读规范,我们获得如下知识:

魔数:4个字节

副版本号:2个字节

主版本号:2个字节

57539ecf8fa47ac802252fc3f368d967.png

然后,在IDE中将魔数标记出来:

8fd71b2512818dcbbed23a81ae796cec.png

然后,在IDE中将版本号标记出来:

f7c64eb00af64cea7cdd925f9fb23f66.png
  • 分析

魔数值和版本号是JVM加载Class文件时的合法性检查依据:

如果16进制文件内容的开始不是CA FE BA BE,则JVM就认为这个文件不是合法的Class文件;

如果16进制文件内容的版本号与JVM版本号不匹配,则JVM就拒绝加载。00 00 00 34,表示Java8对应的JVM可以加载。

  • 技术直男的浪漫

这里值得提到一个趣闻:魔数值CA FE BA BE,应该这样断句:CAFE BABE

Gosling,这位资深技术直男,就是这样温情地形容,他发明的这个编程语言——咖啡宝贝

c53ac7c325be1b7d83bf5cb3a5d76f0a.png

4.信息仓库:常量池

4.1.常量池计数器

在笔者前一篇文章《「编译引擎」学习阅读Class文件结构的意义 》中提到:

常量池:本质是一张表格。

常量:是站在JVM角度看,公用的一些常量值,为了压缩Class文件的大小而存在。

因此,JVM规范才规定Class文件中,有2个字节的常量池计数器(表示常量表格的长度),紧接着的N个字节存储M个常量的具体内容

M个常量:M=常量池计数器的值 - 1

N个字节:N是变长的,因为每个常量类型不同,有的常量需要4个字节存储,有的常量需要很多字节存储。

8ae143d6d8a19ef8753c0bdd33ed7df2.png
  • 首先,找到常量池计数器=00 24,转换为10进制是36,说明常量池中存储了35个常量

一个规范中的细节:The constant_pool table is indexed from 1 to constant_pool_count - 1.

这段话的意思是,常量池计数器值-1就是常量个数

67ba2a095692d2339dec35f4f80abfde.png

4.2.N种常量类型

接下来,我们解读具体的常量,前文讲过,常量有不同的类型,那么Class文件如何表达常量类型呢?

规范中,采用了如下结构表示不同类型的常量:

e84a44bb64351cfd907223fed2c595c3.png

那么,JVM支持多少种类型的常量呢?

058258c58d6edc36857794fd01b23510.png

本文的演练代码中,涉及到了种类型的常量:

CONSTANT_Utf8:1

CONSTANT_String:8

CONSTANT_Class:7

CONSTANT_Fieldref:9

CONSTANT_NameAndType:12

CONSTANT_Methodref:10

4.3.CONSTANT_Utf8类型的常量

JVM规范:The CONSTANT_Utf8_info structure is used to represent constant string values

这种类型的常量,就是表达字符串字面量。

它的结构如下:

a3ea9d6025fcce1ac8a23d635d14f823.png

我们以第25个常量为例,尝试理解一下:

08e4831bb2c714e167f9d1aa97d9747d.png

这个常量值=01 00 0B 68 65 6C 6C 6F 20 77 6F 72 6C 64,如下图所示的含义:

79bc41cdaca64cf1a9462b92c26d7e60.png

第25个常量,表示的就是演练代码中的打印的字符串内容:

d5aebd2fd8ab3caeca140ed884780656.png

4.4.CONSTANT_String类型的常量

JVM规范:The CONSTANT_String_info structure is used to represent constant objects of the type String:

这种类型的常量,是字符串对象类型的,它的值指向4.3中描述的字符串字面量。

它的结构如下:

908edaeaa53c75d37a6fd45157fcbcf1.png

我们以第5个常量为例,尝试理解一下:

5e89849ef32fdea0265659659e044bae.png

这个常量的值= 08 00 19,如下图所示的含义:

9c43b69dd1a6bba14a90baa2ecc4191d.png

结合第5个常量和第25个常量,再看演练代码,我们窥见JVM处理字符串的原理

85aca11d5d62689f0de10f0ade43a0a9.png

4.5.CONSTANT_Class类型的常量

JVM规范:The CONSTANT_Class_info structure is used to represent a class or an interface

这种类型的常量,表示类或者接口

它的结构如下:

398d571faef96f1823bd26f40afcfe15.png

我们以第6个常量为例,尝试理解一下:

41825b8ad97b3064ac581ba5934b7ccf.png

这个常量的值= 07 00 1C,结合第28个常量,如下图所示的含义:

54f0e5e29ba7ece8a45233cd18573ad2.png

再看演练代码,CONSTANT_Class类型的常量表达了演练代码中定义的Demo2类:

c7a12cfc70e5b9bdb723ef4078a921d2.png

4.6.CONSTANT_Fieldref类型的常量

JVM规范:Fields, methods, and interface methods are represented by similar structures:

这种类型的常量,表示字段(例如:一个类中的成员变量)。JVM规范还告诉我们,实例方法、接口方法都是同样的Class格式。

23c9d5466b9e90a4f2e0b8fb85bf4f9f.png

我们以第2个常量为例,尝试理解一下:

c213bc7b49845d6a975117e5497ca89b.png

这个常量的值= 09 00 06 00 16,结合第6个常量和第22个常量,如下图所示的含义:

9b2cddf4a3f3c459ab43cf0262f7fcb6.png

但是第22个常量=0C 00 08 00 09表示了什么呢?我们看下一章节的解读

4.7.CONSTANT_NameAndType类型的常量

JVM规范:The CONSTANT_NameAndType_info structure is used to represent a field or method, without indicating which class or interface type it belongs to

这种类型的常量,表示一个字段的类型信息

a911bd6033e2a5d8e5dd18e178620234.png

我们以第22个常量为例,尝试理解一下:

c66b619e03de651f9d36fb3dde449e3f.png

这个常量的值= 0C 00 08 00 09,结合第8个常量和第9个常量,如下图所示的含义:

22501b8a610fbfba410e568acc15a71e.png

至此,我们就看到了演练代码中在JVM中如何表达int field1信息了:

420333f4d9345d5806dd5699f5018825.png

4.8.CONSTANT_Methodref类型的常量

JVM规范:Fields, methods, and interface methods are represented by similar structures:

这种类型的常量,表示方法。JVM规范还告诉我们,它的结构和字段类型常量都是同样的Class格式。

578386e37812afe84e5d53c4776ffc39.png

我们以第5个常量为例,尝试理解一下:

b559af2ce52e4eb860a14b7725655fd5.png

这个常量的值= 0A 00 1A 00 1B,结合第26、27个常量,如下图所示的含义:

97bd1c8df8b0049ac9fc932c69e5274b.png

5.总结:常量池构建的静态全局观

根据演练代码产生的Class文件,JVM构建了35个常量:

2f7c90b87dcda145025fcc2d1c66d858.png

我们以图形化的方式进一步看看,Class文件的常量池到底为JVM构建了怎样的信息树

5.1.源代码中有哪些方法引用

常量池构建了方法引用的信息树,我们可以看到两颗方法引用树,如下图:

5edf33589bcca775507072aded5e91b6.png

以其中一棵树为例,遍历这棵树,我们可以得到如图的信息:

源代码中有一个方法,此方法属于Object类,方法名为,方法无输入参数,无返回

6f7f746172d62ddb0f6663bde4d402ff.png

5.2.源代码中有哪些字段引用

常量池构建了字段引用的信息树,我们可以看到两颗字段引用树,如下图:

305db514fce1942ecd81a8c900d4ea80.png

以其中一棵树为例,遍历这棵树,我们可以得到如图的信息:

源代码中有一个字段,此字段属于Demo2类,字段名为field1,字段数据类型为int

1c2bb66be939770d40f163a9eb101438.png

5.3.源代码中有哪些字符串对象

常量池构建了字符串类型对象的信息树,我们可以看到一颗字符串对象树,如下图:

ea93ab7c4f1d7be04bbed3ff56fda800.png

以这棵树为例,遍历这棵树,我们可以得到如图的信息:

源代码中有一个字符串对象,此字符串对象的值是hello world

4068617c11c40afb1e4176b843746a89.png

5.4.源代码中其它的字符串字面量

643c11e13e468215b4713ad65174a90c.png

5.5.静态的全局观

至此,我们已经看到了常量池为JVM诠释了Java源代码中的信息树。

常量池中的这些信息树,为JVM构建出了一个静态的全局观

通过这些信息树,我们已经能反演出Java源代码中的静态结构

有几个类?

类中有几个成员变量?

类中有几个方法?

每个成员变量的名称、数据类型

每个方法的名称、输入参数列表、返回值类型

但常量池中还有一些信息孤点,我们无法反演出一些动态结构

成员变量的状态变化过程

方法体内的运行过程

方法内局部变量的变化过程

6.下一步

笔者下篇会继续阅读本演练代码对应的Class文件剩余部分,包括

访问标识

类索引

字段表

方法表

属性表

进一步理解Class字节码如何为JVM构建动态全局观

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值