第六章 类文件结构-Class类文件的结构 《深入理解jvm虚拟机》

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/xuchuanliang11/article/details/102540740

java虚拟机实现语言无关性,任何语言只要能最终编译生成java虚拟机能够识别的字节码文件,即可最终运行在java虚拟机上,实现语言无关性的的基础是虚拟机和字节码。

java语言实现跨平台,实际上是借助java虚拟机在不同平台上能够正常运行。

Class文件结构

Class文件是一组以8位字节为基础单位的二进制流,各个项目紧密排列,无间隔符号。当遇到需要占用8位字节以上空间的数据项时,则会按照高位在前的方式分割成若干个8位字节进行存储。

Class数据流中只有两种数据类型:无符号数和表。

无符号数属于基本的数据类型,以u1,u2,u4,u8来分别代表1个字节,2个字节,4个字节,8个字节的无符号数,无符号数可以用来表示数字、索引引用、数量值或按照UTF-8编码构成字符串值。

表是由多个无符号数或者其他表作为数据项构成的复合数据类型,所有的表习惯以_info结尾。表用于表示有层次关系的复合结构的数据,整个Class文件本质上就是一张表。

无论是符号数还是表,当需要描述同一类型但数量不定的多个数据时,经常会使用一个前置的容量计数器加若干个连续的数据项的形式,称之为某一类型的集合。

魔数与Class文件版本

每个Class文件的头四个字节称为魔数(Magic number),作用就是确定这个文件是否为一个能被虚拟机接受的Class文件。使用魔数而不是使用扩展名来进行识别主要是基于安全方面的考虑,因为扩展名可以随意改动。魔术:0XCAFEBABE。

紧接着魔数的4个字节存储的是Class文件的版本号,第5和第6个字节是次版本号,第7和第8个字节是主版本号。java版本号从45开始,每个大版本加1,jdk向前兼容,不向后兼容。

以书中的字节码为例,进行解析:

从第一位开始:0XCAFEBABE是模数,0X0000是次版本号,0X0032是主版本号,也就是十进制的50,那么就是jdk1.6编译出来的字节码。

常量池

紧接着主版本号之后是常量池入口,是Class文件中第一个出现的表结构数据。由于常量池中常量的数量是不固定的,所以常量池的入口需要放置一项u2类型的数据,代表常量池容量计数值,常量池容量计数值从1开始,而不是从0开始计数。也就是上图中的0X0016,也就是十进制的22,表示常量池有21个常量,索引范围是1~21。

常量池中存放两大类常量:字面量和符号引用。字面量接近java中常量的概念,如文本字符串、声明为final的常量池等。而符号引用包括:1.类和接口的全限定名;2.字段的名称和描述符;3.方法的名称和描述符;

常量池中每一个常量都是一个表,一共14个表,有一个共同特点:表开始的第一位是一个u1类型的标志位(tag),代表这个当前这个常量属于哪一种常量类型,见下图:

我们查看前面字节码可知,在常量池容量计数值后面的u1是0X07,查询上方常量池项目类型得知是CONSTANT_Class_info,CONSTANT_Class_info的结构参见下图:

tag是标志位,用于区分常量类型,前面已经讲过是0X07;name_index是一个索引值,它指向常量池中一个CONSTANT_Utf8_info类型常量,此常量标识一个类或者接口的全限定名,这里的tag是0X00,name_index是0X0002,也即指向了常量池中第二项常量,它的标志位是0X01,查表可知是一个CONSTANT_Utf8_info类型的常量。CONSTANT_Utf8_info的结构如下图:

length表示这个URF-8编码的字符串长度是多少字节,它后面紧跟着长度为length字节的连续数据是一个使用UTF-8缩略编码表示的字符串。备注:由于Class文件中方法、字段等都需要引用CONSTANT_Utf8_info型常量来描述名称,而且CONSTANT_Utf8_info型常量的最大长度是u2也就是十六个字节,即能表示最大值是65535,1KB=1024字节,也就是最多2的8次方即64KB;

本例中的length值是0X001D,也就是29字节,往后查找29个字节,转成UTF-8就是org/fenixsoft/clazz/TestClass。

剩余的常量类似于这种解析方式。

java提供javap专门用来分析Class文件字节码工具。javap -verbose TestClass

java中14种常量项的结构总表

访问标志

常量池结束后,紧接着两个字节代表访问标志(access_flags),这个标志用于识别一些类或者接口层次的访问信息,包括:这个Class是类还是接口;是否定义为public类型;是否定义为abstract类型,参见下图:

access_flags中一共有16个标志位可以使用,当前只定义了8个,没有使用到的标志位一律为0,以TestClass为例,他的ACC_PUBLIC ACC_SUPER为真,ACC_FINAL ACC_INTERFACE ACC_ABSTRACT ACC_SYNTHETIC ACC_ANNOTATION ACC_ENUM应该为假

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

类索引和父类索引都是一个u2类型的数据,而接口索引集合是一组u2类型的数据的集合,具体规则以及细节参见书本,实际和上方的基本推导方式类型,均是按照一定的规则能够解析出父类或接口的全限定名。

字段表集合

包括:字段的作用域(public、private、protected)、是实例变量还是类变量(static)、可变性(final)、并发可见性(volatile)、可否被序列化(transient)、字段数据类型(基本数据类型、对象、数组);具体解析方式参见该书本

方法表集合

方法表结构包括:访问标志、名称索引、描述符索引、属性表集合,具体解析方式参见该书本。

方法里的java代码,经过编译器编译成字节码指令后,存放在方法属性表集合中一个名为Code的属性表中。

属性表集合

猜你喜欢

转载自blog.csdn.net/xuchuanliang11/article/details/102540740