Class类文件结构的理解

目录

一、magic

二、minor_version、major_version

三、constant_pool_count、constant_pool

四、access_flags

五、this_class、super_class

六、interfaces_count、interfaces

七、fileds_count、fields

八、methods_count、methods

九、attributes_count、attributes


本文大部分都是参考《深入理解Java虚拟机 JVM高级特性与最佳实践》一书,写下这篇博客的主要目的主要是加深自己的理解。

 

首先是类的整体结构

其中,u1、u2、u4、u8表示1个字节、2个字节、4个字节和8个字节的无符号数。

文章的作者举了一个例子来进行讲解,我也直接引用他的例子。

package com.zhuyun.Maven;

public class TestClass {

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

其编译后生成的Class内容如下:

下面按照Class结构的顺序进行分析。

一、magic

最开始的4个字节是魔数0xCAFEBABE

二、minor_version、major_version

接下的4个字节是次版本号和主版本号,分别是0x0000、0x0034,其对应的十进制版本号是52,对应关系如下图。

对应的JDK版本号是1.8。

三、constant_pool_count、constant_pool

接下的2个字节和不定长字节,表示常量池。

前两个字节表示常量池中常量的个数,从1开始计数,所以一共有21项常量。

这些常量的第一字节是标识位,代表当前常量是属于哪种常量类型,其对应关系如下:

第一个常量:0x07

从表中可以看出,它的常量类型是CONSTANT_Class_info,其结构如下:

tag表示常量类型,就是前面的0x07。

name_index表示索引值,0x0002表示常量池的第二个常量,所以继续往后找。

 

第二个常量:0x01

 从表中可以看出,0x01的常量类型是CONSTANT_Utf8_info,其结构如下:

第一个字节是tag(0x01),后两个字节表示UTF-8的字符串长度,0x001a,十进制是26。

接下来的26个字节,就是 字符串的内容com/zhuyun/Maven/TestClass。

第三个常量:0x07

常量类型是CONSTANT_Class_info,索引值是4,也就是接下来的一个变量。

第四个常量:0x01

常量类型是CONSTANT_Utf8_info,字符串长度是0x10,十进制是16,字符串内容是java/lang/Object 。

第五个常量:0x01

常量类型是CONSTANT_Utf8_info,字符串长度是0x01,十进制是1,字符串内容是m。

第六个常量:0x01 

常量类型是CONSTANT_Utf8_info,字符串长度是0x01,十进制是1,字符串内容是I。

第七个常量:0x01 

常量类型是CONSTANT_Utf8_info,字符串长度是0x0006,十进制是6,字符串内容是<init>。

 第八个常量:0x01 

常量类型是CONSTANT_Utf8_info,字符串长度是0x0003,十进制是3,字符串内容是()V。

第九个常量:0x01 

常量类型是CONSTANT_Utf8_info,字符串长度是0x0004,十进制是4,字符串内容是Code。

第十个常量:0x0a

常量类型是CONSTANT_Methodret_info,其结构如下:

第一个字节是0x0a,表示常量类型是CONSTANT_Methodret_info。

接下来2个字节表示索引值0x0003,也就是指向了第三个变量,即前面的 java/lang/Object,表示方法所属的类。

接下来2个字节表示索引值0x000b,也就是指向了第11个变量,即下一个变量。

第十一个常量:0x0c

常量类型是CONSTANT_NameAndType_info,其结构如下:

 第一个字节是0x0c,表示常量类型是CONSTANT_NameAndType_info。

接下来2个字节表示索引值0x0007,也就是指向了第7个变量,即前面的<init>,表示方法的名称。

接下来2个字节表示索引值0x0008,也就是指向了第8个变量,即前面的()V,表示刚方法的返回类型是void。

第十二个常量:0x01

常量类型是CONSTANT_Utf8_info,字符串长度是0x000f,十进制是15,字符串内容是LineNumberTable。

第十三个常量:0x01

常量类型是CONSTANT_Utf8_info,字符串长度是0x0012,十进制是18,字符串内容是LocalVariableTable。

 第十四个常量:0x01 

常量类型是CONSTANT_Utf8_info,字符串长度是0x0004,十进制是4,字符串内容是this。

第十五个常量:0x01 

常量类型是CONSTANT_Utf8_info,字符串长度是0x001c,十进制是28,字符串内容是Lcom/zhuyun/Maven/TestClass;。

第十六个常量:0x01  

常量类型是CONSTANT_Utf8_info,字符串长度是0x0003,十进制是3,字符串内容是inc。

 第十七个常量:0x01  

常量类型是CONSTANT_Utf8_info,字符串长度是0x0003,十进制是3,字符串内容是()I。

 第十八个常量:0x09 

常量类型是CONSTANT_Filedref_info,表示字段的符号引用,其结构如下。

 第一个字节是0x09,表示常量类型是CONSTANT_Filedref_info。

接下来2个字节表示索引值0x0001,也就是指向了第1个变量,即前面的 com/zhuyun/Maven/TestClass,表示字段所属的类。

接下来2个字节表示索引值0x0013,也就是指向了第19个变量,即下一个变量,表示了该字段的描述符。

 第十九个常量:0x0c

 常量类型是CONSTANT_NameAndType_info,其结构如下:

 第一个字节是0x0c,表示常量类型是CONSTANT_NameAndType_info。

接下来2个字节表示索引值0x0005,也就是指向了第5个变量,即前面的m,表示该字段的名称。

 

接下来2个字节表示索引值0x0006,也就是指向了第6个变量,即前面的I,表示该变量的数据类型是int。

 

 第二十个常量:0x01

常量类型是CONSTANT_Utf8_info,字符串长度是0x000a,十进制是10,字符串内容是SourceFile。

 第二十一个常量:0x01

常量类型是CONSTANT_Utf8_info,字符串长度是0x000e,十进制是14,字符串内容是TestClass.java。

 四、access_flags

下面2个字节是访问标志,用于识别一些类或者接口层次的访问信息,包括:这个class是类还是接口;是否定义为public类型;是否定义为abstract类型;是否被声明为final等。

首先,将0x0021转成二进制

0000 0000 0010 0001

 从高到低,标志位依次的含义:

1 
2ACC_ENUM
3ACC_ANNOTATION
4ACC_SYNTHETIC
5 
6ACC_ABSTRACT
7ACC_INTERFACE
8 
9 
10 
11ACC_SUPER
12ACC_FINAL
13 
14 
15 
16ACC_PUBLIC

所以只有第11位和13位是1,也就是ACC_SUPER和ACC_PUBLIC为真。

 

五、this_class、super_class

接下来4个字节表示类引用以及父类引用。除了java.lang.Object外,所有Java类都有父类,因此除了java.lang.Object外,所有Java类的父类引用都不为0。

从前面的常量池中可以找到,类引用是com/zhuyun/Maven/TestClass,而父类引用是java/lang/Object。

 

六、interfaces_count、interfaces

接下来是接口索引集合,用来描述这个类实现了哪些接口。在该例子中,未实现任何接口所以前2个字节是0,后面的interface也就不占用任何字节。

 

七、fileds_count、fields

前2个字节表示字段的个数,该例子中只有一个m字段,所以是1。

后面表示字段表集合,用来描述接口或者类中声明的变量。其结构如下:

前2个字节表示访问标志,0x0002表示字段是private,其他访问标志都是false,其含义如下:

接下来的name_index和descriptor_index表示字段的名称以及字段的描述符的索引。

从前面的常量表中可以知道,该字段的名称是第五个常量m,字段的类型是第六个常量I,其对应的基本类型是int。

接下来的2个字节表示属性表集合,用于存储一些额外的信息。在本例中属性表计数器为0,没有额外描述信息。

 

八、methods_count、methods

前2个字节表示方法的个数,该字段是0x0002,所以有两个方法,一个是本身的方法inc(),另一个自动添加的构造方法<init>。

 

后面表示方法表集合,与上面的字段表类似,其结构如下:

第一个方法:

前2个字节表示访问标志,0x0001表示方法是public的,其他访问标志都是false,其含义如下:

 

接下来的name_index和descriptor_index表示方法的名称以及方法的描述符的索引。

从前面的常量表中可以知道,该方法的名称是第7个常量<init>,方法的描述是第8个常量()V,表示参数为空,返回值为void。

 

九、attributes_count、attributes

属性表集合,其结构如下:

接下来的2个字节表示属性表集合,用于存储一些额外的信息。0x0001表示只有一个属性表。

接下来2个字节表示属性名称索引,0x0009,对应的常量是Code,说明此属性是方法的字节码描述。

其结构如下:

attribute_name_index:前2个字节,也就是0x0009,表示Code

attribute_length:接下来4个字节,0x0000002f,表示属性值长度。

max_stack:操作数栈深度的最大值,该例子中为1。

max_locals:局部变量所需的存储空间,单位是slot,该例子中为1。

code_length: 字节码长度,该例子中为5,表示有5条字节码指令。

code:字节码指令,根据前面的code_length,获取接下来的code_length个字节作为字节码指令。

      查表可知:

      

        

        

         

exception_table_length:异常处理表长度,该例子中不存在异常处理,长度为0。

 

接下来解析类似。

 

接下来的2个字节表示属性表集合,用于存储一些额外的信息。0x0002表示有2个属性表。

接下来2个字节表示属性名称索引,0x000c,对应的常量是LineNumberTable,此属性是用于描述Java源码行号与字节码行号之间的对应关系。

其结构如下:

attribute_name_index:前2个字节,也就是0x000c,表示LineNumberTable

attribute_length:接下来4个字节,0x00000006,表示属性值长度,目前是6个字节。

line_number_table_length:接下来的2个字节,0x0001,表示line_number_info的个数。

line_number_info:包括了start_pc和line_number两个u2类型的数据项,前者是字节码行数,后者是Java源码行号。该例子中                                       其值是0x0000 0003。

 

继续往后解析,接下来2个字节表示属性名称索引,0x000d,对应的常量是LocalVariableTable,此属性是用于描述栈帧中局部变量表中的变量Java源码中定义的变量之间的关系。

其结构如下:

attribute_name_index:前2个字节,也就是0x000d,表示LocalVariableTable

attribute_length:接下来4个字节,0x0000000c,表示属性值长度,目前是12个字节。

local_variable_table_length:接下来的2个字节,0x0001,表示local_variable_table的个数。

local_variable_table:代表了一个栈帧与源码中的局部变量的关联,结构如下:

start_pc:这个局部变量的生命周期开始的字节码偏移量。该例子中为0x0001。

length:这个局部变量的生命周期开始的字节码的作用范围覆盖的长度。该例子中为0x0000。

name_index:局部变量名称的索引,该例子中为0x0005,具体的值为m。

descriptor_index:局部变量的描述符的索引,该例子中为0x000e,具体的值为this。

index:局部变量在栈帧局部变量表中Slot的位置,该例子中为0x000f。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值