Java class文件格式之属性详解

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

Java class文件格式之属性详解

Code属性

code属性是方法的一个最重要的属性。因为它里面存放的是方法的字节码指令,除此之外还存放了和操作数栈,局部变量相关的信息。所有不是抽象的方法,都必须在method_info中的attributes中有一个Code属性。下面是Code 属性的结构,为了更直观的展示Code属性和method_info的包含关系,特意画出了method_info:

下面依次介绍code属性中的各个部分。

attribute_name_index指向常量池中的一个CONSTANT_Utf8_info ,这个CONSTANT_Utf8_info 中存放的是当前属性的名字“Code” 。

attribute_length给出了当前Code属性的长度(不包括前六字节)。

max_stack指定当前方法被执行引擎执行的时候,在栈帧中需要分配的操作数栈的大小。

max_locals指定当前方法被执行引擎执行的时候,在栈帧中需要分配的局部表量表的大小。注意,这个数字并不是局部变量的个数,因为根据局部变量的作用域不同,在执行到一个局部变量以外时,下一个局部变量可以重用上一个局部变量的空间(每个局部变量在局部变量表中占用一个或两个Slot)。方法中的局部变量包括方法的参数,方法的默认参数this,方法体中定义的变量,catch语句中的异常对象。关于执行引擎的相关内容会在后面的博客中讲到。

code_length指定该方法的字节码的长度,class文件中每条字节码占一个字节。

code存放字节码指令本身,它的长度是code_length个字节。

exception_table_length指定异常表的大小

exception_table就是所谓的异常表,它是对方法体中try-catch_finally的描述。exception_table可以看做是一个数组,每个数组项是一个exception_info结构,一般来说每个catch块对应一个exception_info,编译器也可能会为当前方法生成一些exception_info。 exception_info的结构如下(为了直观的显示exception_info, exception_table和Code属性的关系,画出了Code属性,的话读者就会更清楚各个数据项之间的位置关系和包含关系):

下面讲解exception_info中的各个字段的意思。

start_pc是从字节码(Code属性中的code部分)起始处到当前异常处理器起始处的偏移量。

end_pc是从字节码起始处到当前异常处理器末尾的偏移量。

handler_pc是指当前异常处理器用来处理异常(即catch块)的第一条指令相对于字节码开始处的偏移量。

catch_type是一个常量池索引,指向常量池中的一个CONSTANT_Class_info数据项,该数据项描述了catch块中的异常的类型信息。这个类型必须是ng.Throwable的或其子类。

所以可以总结,一个异常处理器(exception_info)的意思是:如果偏移量从start_pc到end_pc之间的字节码出现了catch_type描述的类型的异常,那么就跳转到偏移量为handler_pc的字节码处去执行。如果catch_type为0,就代表不引用任何常量池项(再回顾一下,常量池中的项是从1开始计的),那么这个exception_info用于实现finally子句。

我们一直在介绍Code属性,只不过刚才进行了一个小插曲,介绍了Code属性中的exception_table中的exception_info 的详细信息。下面我们继续介绍Code 属性中的其他信息,希望读者不要被绕晕了: )

attributes_count 表示当前Code 属性中存在的其他属性的个数。现在我们知道,class中的属性,不仅会出现在顶层的class中,会存在field_info中,会存在method_info中,甚至还会出现在属性中。

attributes可以看做是一个数组,里面存放了Code属性中的其他属性。Code 属性中可以出现的其他属性有LineNumberTable和LocalVariableTable 。这两个属性会在下面介绍。

LineNumberTable属性

LineNumberTable属性存在于Code属性中,它建立了字节码偏移量到源代码行号之间的联系。这个属性是可选的,编译器可以选择不生成该属性。下面是该属性的结构(同样给出了全局的位置关系,LineNumberTable在图的右下角部分):

由于这个属性并不是重点,我们在此简单的讲述。

每个LineNumberTable中的line_number_table部分,可以看做是一个数组,数组的每项是一个line_number_info ,每个line_number_info 结构描述了一条字节码和源码行号的对应关系。其中start_pc是这个line_number_info 描述的字节码指令的偏移量, line_number是这个line_number_info 描述的字节码指令对应的源码中的行号。可以看出,方法中的每条字节码都对应一个line_number_info ,这些line_number_info 中的line_number可以指向相同的行号,因为一行源码可以编译出多条字节码。

LocalVariableTable属性

LocalVariableTable 属性建立了方法中的局部变量与源代码中的局部变量之间的对应关系。这个属性存在于Code属性中。这个属性是可选的,编译器可以选择不生成这个属性。该属性的结构如下:(同样给出了全局的位置关系图,LocalVariableTable 在该图的右下角)

由于这个属性相对不那么重要,这里只是大概讲解一下。

每个LocalVariableTable 的local_variable_table部分可以看做是一个数组,每个数组项是一个叫做local_variable_info 的结构,该结构描述了某个局部变量的变量名和描述符,还有和源代码的对应关系。下面讲解local_variable_info的各个部分:

start_pc是当前local_variable_info所对应的局部变量的作用域的起始字节码偏移量;

length是当前local_variable_info所对应的局部变量的作用域的大小。也就是从字节码偏移量start_pc 到start_pc+length就是当前局部变量的作用域范围;

name_index指向常量池中的一个CONSTANT_Utf8_info,该CONSTANT_Utf8_info描述了当前局部变量的变量名;

descriptor_index指向常量池中的一个CONSTANT_Utf8_info,该CONSTANT_Utf8_info描述了当前局部变量的描述符;

index描述了在该方法被执行时,当前局部变量在栈帧中局部变量表中的位置。

由此可知,方法中的每个局部变量都会对应一个local_variable_info 。

相关文档
最新文档