[Class文件结构]3——方法表、属性表

前言

[Class文件结构] 2 - 常量池、字段表对常量池、字段表等进行了阐述,本文继续分析Class文件,阐述方法表、属性表等。

方法表

方法表结构如下图所示:

和字段表类似,依次包含访问标志(access_flags)、名称索引(name_index)、描述符索引(descriptor_index)、属性表集合(attributes)几项。

因为volatile关键字和transient关键字不能修饰方法,所以方法表中的访问标志没有这两个。与之相对的,synchronized、native、strictfp和abstract关键字可以修饰方法,方法表标志位取值如下:

查看示例Class文件,第1个u2类型的数据值为2,表示集合中有2个方法(一个是类默认的init构造方法,另一个是inc方法)。第1个方法的访问标志为1,则是使用了ACC_PUBLIC的标志,名称索引为7,查看常量池可知方法名为 <init> ,描述符索引值为8,对应常量为 ()V ,属性表计数器 attrbutes_acount值为1,表示此方法有一个属性,属性名称索引为9,对应常量为 Code。关于Code,下文会详述。

对照 javap 指令结果查看,更清晰。

Code,代码,顾名思义就是存储方法中的代码,Java代码经过编译器编译成字节码指令之后,存放在Code属性中。并不是直接存储的Java代码。

属性表集合

属性表(attribute_info)在Class文件、字段表、方法表中都可以携带自己的属性表集合,以用于描述某些场景专有信息。

与其它数据项目要求的顺序、长度、内容不同,属性表集合的要求稍微宽松,不再要求各个属性表具有严格的顺序,并且只要不与已有的属性名重复,编译器还可以向属性表中写入自已定义的属性信息,Java虚拟机能忽略掉它不认识的属性。下图是 Java虚拟机规范 一文中预定义的9项虚拟机能识别的属性。

对于每个属性,它的名称需要从常量池中引用一个 CONSTANT_Utf_info 类型的常量来表示,而属性值的结构则是完全自定义的,只需要说明属性所占用的长度即可,一个规范的属性表应该满足下面所示结构。

Code属性

Java方法内代码经过Javac编译处理后,最终变成字节码指令存储在Code属性内。Code属性出现在方法表的属性集合中,但并非所有方法表都必须存在这个属性。接口或抽象类的方法就不存在Code属性。Code属性结构如下:

attribute_name_index是一项指向CONSTANT_Utf8_info型常量的索引,常量值固定为Code。

attribute_length指示了属性的长度。

max_stack代表了操作数栈深度的最大值,在方法执行的任意时刻,操作数栈不会大于这个深度。

max_locals代表了局部变量所需要的存储空间。max_locals单位是slot,slot是虚拟机为局部变量分配内存所使用的最小单位。对于byte,char,float,int,short,boolean,reference,return Address 等长度不超过32位的数据类型,每个局部变量使用1个slot,而double和long这两种64位数据类型则使用2个slot。注意,slot可以重用,当代码执行超出一个局部变量的作用域时,这个局部变量所占用的slot就可以被其它局部变量使用。

code_length和code用于存储Java源程序编译后生成的字节码指令。code_length代表字节码长度,code用于存储字节码指令的一系列字节流。code由u1表示,虚拟机讲到一个字节码时就知道怎么理解,后续带什么参数等等。u1的取值是0到255,也就是说一共可以表达255条指令。

code有点类似cpu上的指令集,例如+号会被编译成 iadd 虚拟机字节码指令。

code_length由一个u4表示,理论上最大值是232-1,但虚拟机规范中限制方法不能超过65535。

以示例Class为例,分析其init方法。

max_stack和max_locals值都为1,code_length值为5,则表示接下来有5个字节码指令。

字节码指令的具体定义,请自行搜索查询,较为繁杂,本文中不再细说,大体情况如上文所言,将一行行Java代码通过编译成特定的指令集,虚拟机能懂的指令集。对照 javap 查看init方法内容

有读者可能会问了,明明是5个指令,为啥上边只显示3个指令,0、1、4还有2和3哪去了呢?00和0A是方法的参数,0A通过常量池很容易就能查询到,是init方法的描述符。而00在常量池中没有,其实它表示this指针。

注意到 javap 内容中显示的args_size = 1,明明init方法没有参数,怎么会值为1呢?其实就是因为非static函数自带this指针为参数,可以访问对象本身。

异常表

异常表就是方法中的异常处理内容,try catch代码块。它的结构如下所示:

它表示,如果字节码从start_pc行到end_pc行之间出现类型为catch_type或其子类的异常,则转到handler_pc行继续处理。具体字节码指令不再分析。

Exceptions属性

Exceptions是在方法表中与Code属性平级的一项属性,与异常表不一样,异常表是Code的下级属性。Exceptions属性的作用是列举出方法中可能抛出的受查异常,也就是 throws 关键字后列表的异常,它的结构表如下:

number_of_exceptions项表示方法有可能抛出多少种异常,每一种异常由一个exception_index_table项表示,exception_index_table是一个指向常量池中 utf8 类型的索引。

LineNumberTable属性

LineNumberTable属性用于描述 Java 源码行号与字节码行号 之间的对应关系。它不是必须属性,如果不生成它,那么产生异常后,堆栈中将不会显示出错的行号,并且在调试时也无法在源码中设置断点。

line_number_table是一个数量为line_number_table_length、类型为line_number_info的集合,line_number_info表包括了start_pc和line_number两个u2数据项,前者是字节码行号,后者 是Java源码行号。

查看javap内容:

javap中显示 line 8:0,第8行正好是return所在的行号。

LocalVariableTable属性

LocalVariableTable用于描述栈桢中局部变量表中的变量与Java源码中定义的变量之间的关系。也不是必须的,它的结构如下:

其中local_variable_info项目代表了一个栈桢与源码中局部变量的关联,结构如下:

start_pc和length属性分别代表这个局部变量的生命周期的字节码偏移量及其作用范围覆盖的长度,两者结合起来就是这个局部变量在字节码之中的作用域范围。

name_index和descriptor_index都是指向常量池中 utf8 类型的常量,分别代表局部变量的名称及局部变量的描述符。

index是这个局部变量在栈桢局部变量表中slot的位置,如果是64位类型,则它占用的slot为index 和 index+1的两个位置。

如果将inc方法中添加一个参数,如下图所示:

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

查看javap内容

LocalVariableTable说明了方法内局部变量的名字,占用空间以及描述符(参数是什么类型等等)。

SourceFile属性

SourceFile属性,用于记录Class文件的源码文件名称。

其它

属性表还有几个属性,比如 ConstantValue和 Inner Class。可以自己写代码,查看javap内容学习。值得一提的是,对于类变量也就是static变量,如果变量被static和final同时修饰并且是基本数据类型或者String类型的话,那么此变量使用 ConstantValue 属性进行初始化,如果此变量没有被final修饰,或者并非基本类型及字符串,则选择在 <clinit> 方法中进行初始化。

总结

Class文件结构真心复杂,很多都是要记忆的,也很枯燥,但是要学习虚拟机原理,必须清楚Class文件的结构,幸好有javap这个工具,多看看javap信息,有助于理解Class文件内容。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容