实例分析Class字节码文件(三)

上篇必看:实例分析Class字节码文件(二)

六、属性表集合

在前面介绍的,字段表集合和方法表集合中都有使用到属性表,结构均为:

这里写图片描述

首先用2个字段标识属性表集合的大小,之后就是属性表的具体内容,属性表结构如下:

这里写图片描述

attribute_name_index : 指向运行时常量池中的某个常量
attribute_length : 属性长度
info:属性具体内容

下面开始介绍,java虚拟机可识别的属性

1.Code属性

Java程序方法体中代码经过编译后,最终变成字节码指令存储在该属性中,Code属性出现在方法表的属性集合之中,但并非所有的方法表都必须存在这个属性,譬如接口或抽象类中的方法就不存在Code属性

Code属性表结构如下:

这里写图片描述

attribute_name_index : 属性名称,指向常量,固定值为Code

attribute_length : 属性值的长度

max_stack : 操作数栈(Operand Stacks)深度的最大值,在方法执行的任意时刻,操作数栈都不会超过这个深度。虚拟机运行的时候需要根据这个值来分配栈帧(Stack Frame)中的操作栈深度

max_locals : 局部变量表所需的空间,单位是Slot,Slot是虚拟机为局部变量表分配内存所使用的最小单位。对于bytecharfloatintshortbooleanreturnAddress等长度不超过32位的数据结构,占用一个Slot,而doublelong这种64位的数据结构需要两个Slot。

注:局部变量表存放的数据包括:
1、方法参数(包括实例方法中隐藏的参数this)
2、try-catch块,catch定义的异常
3、方法体中的局部变量

另外,并不是方法中用到了多少个局部变量,就把这些局部变量所占Slot之和作为max_locals的值,原因是局部变量表中的Slot可以复用。例如if语句

code_length、code:是用来存储Java源程序编译后生成的字节码指令。前者代表字节码长度,后者是用于存储字节码指令的一系列字节流。每个指令用一个u1类型的单字节表示,当虚拟机读取到code中的一个字节码时,就可以对应找到这个字节码代表是是什么指令,并可以知道这个指令后面是否需要跟随参数,以及参数应当如何理解。

u1表示8bit,0~255,一共可以表示256个指令。

注:目前,jvm已经定义了其中约200条编码值对应的指令含义

code_length是一个u4类型,理论上指令数量可以为2的32次方减1,但是在虚拟机规范中明确限制了一个方法不允许超过65535个指令,实际上只使用了u2的长度。

继续分析字节码文件,方法的code属性为例:

max_stack : 00 01
max_locals : 00 01
code_length : 00 00 00 05
code : 2a b7 00 0a b1

通过javap 查看如下:

这里写图片描述

通过上图发现:args_size = 1,即方法参数有一个,即this,局部变量表第一个Slot就是来存放指向当前对象实例的局部变量(this)

在code后为异常信息,该实例中无异常信息。重新举例如下:

public class TestClass {

    public int inc(){

        int x;

        try {
            x = 1;
            return x;
        } catch (Exception e) {
            x = 2;
            return x;
        } finally{
            x = 3;
        }

    }
}

对应获取字节码信息如下:

这里写图片描述

2.Exception属性

用于列举出方法中可能抛出的受查异常,也就是方法描述时,在throws关键字后面列举的异常。结构如下:

这里写图片描述

这里写图片描述

attribute_name_index : 属性名称,指向常量
attribute_length : 长度
number_of_exceptions : 抛出的异常数
exception_index_table : 异常的长度,指向常量

3.LineNumberTable属性

用于描述Java源码行号与字节码行号之间的对应关系。可设置在Class文件中是否生成该属性,如果不生成,当抛出异常时,堆栈中将不会显示出错的行号。结构如下:

这里写图片描述

attribute_name_index : 属性名,对应常量
attribute_length : 长度
line_number_table_length : 对应关系的数量
line_number_table : 具体对应关系

4.LocalVaridbleTable属性

用于描述描述栈帧中局部变量与Java源码中定义的变量之间的关系。可设置在Class文件中是否生成该属性,如果不生成,方法的参数名称会丢失。IDE将会使用诸如arg0、arg1之类的占位符代替原有的参数名。结构如下:

这里写图片描述

local_variable_table结构如下:
这里写图片描述

start_pc 、length: 该局部变量起作用开始代码的位置,作用覆盖的长度,两者结合起来就是这个局部变量的作用域范围。

name_index、descriptor_index : 局部变量名称,局部变量的描述符

index : 该局部变量在局部变量表中Slot的位置。当这个变量类型是64位时(double和long),它占用Slot的index和index+1两个

5.SourceFile属性

用于记录生成这个Class文件的源码文件名称。可设置在Class文件中是否生成该属性,如果不生成,当抛出异常时,堆栈中将不会显示出错误代码所属的文件名(一般情况下,类名文件名是一致的),结构如下:

这里写图片描述

attribute_name_index : 属性名称
attribute_length : 长度
sourcefile_index : 源文件名

6.ConstantValue属性

通知虚拟机自动为静态变量赋值。只有被static关键字修饰的变量(类变量)才能使用这个属性。(static不能修饰局部变量)

注:
① 非static修饰变量的赋值在实例构造器<init>方法中进行
②static修饰变量(类变量)的赋值有两种方式选择:类构造器<clinit>方法中或者使用ConstantValue属性

结构如下:

这里写图片描述

attribute_name_index : 属性名称
attribute_length : 需要初始化的变量个数
constantvalue_index : 需要初始化变量名称

7.InnerClasses属性

用于记录内部类与宿主类之间的关联。如果一个类中定义了内部类,那编译器将会生成该属性。结构如下:

这里写图片描述

attribute_name_index : 属性名称
attribute_length :长度
number_of_classes : 内部类个数
inner_classes : 用于描述关系

inner_classes_info结构如下:

这里写图片描述

inner_class_info_index : 内部类符号引用
outer_class_info_index : 外部类符号引用
inner_name_index : 内部类名称
inner_class_access_flags: 内部类访问标志

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值