手把手教你撸一个Mini JVM系列(3)之解析Class File -- 字段、方法、属性

引子:

在常量池, 访问修饰符, 类和接口后面紧跟的内容是字段和方法, 这两个结构是最复杂的, 因为其里面包含有属性这个成员, 而属性又是可以嵌套的.

1. 字段

和之前的常量池一样, 因为每个class中字段的数量是不确定的, 所以字段部分的开头两个字节用于表示当前class文件中的字段的个数, 紧跟着的才是具体的字段.

先来看一下字段的结构

    Field_Info {
        u2 access_flag;
        u2 name_index;
        u2 descriptor_index;
        u2 attribute_count;
        attribute_info attributes[attribute_count];
    }
  • access_flag表示该字段的访问修饰符, 字段的访问修饰符和类的表示方式相似, 但是具体的内容不一样

    字段的访问标识

    图1-1 FIELD-ACCESS-FLAG
  • name_index指向常量池中的name_index索引的常量项

  • descriptor_index指向常量池中的descriptor_index索引的常量项

  • attribute_count表示该字段的属性个数

  • attributes[attribute_count]表示该字段的具体的属性

注意: 这里字段的descriptor代表的字段的类型, 但是类型不是写代码的时候int, String这样整个单词的, 它是一些字符的简写, 如下:

图1-2 DESCRIPTOR

所以, 举个例子如果字段是String类型, 那么它的descriptor就是Ljava/lang/Object;如果字段是int[][], 那么它的descriptor就是[[I

对于属性的解释放到和方法属性一起

2. 方法

方法和字段一样, 也需要有一个表示方法个数的字段, 同时这个字段后面紧跟的就是具体的方法

同样, 来看一下方法的结构:

    Method_Info {
        u2 access_flag;
        u2 name_index;
        u2 descriptor_index;
        u2 attribute_count;
        attribute_info attributes[attribute_count]
    }
  • access_flag的意义和之前field一样, 只不过取值不同, method的access flag可以取的值如下:

    图1-3 METHOD-ACCESS-FLAG

<div style="margin-left:200px"></div>

  • name_index的意义和field的也一样, 表示了方法的名称

  • descriptor_index的意义和field也一样, 只不过其表示方法不同, 让我们来看一下它是如何表示的:

    method的descriptor由两部分组成, 一部分是参数的descriptor, 一部分是返回值的descriptor, 所以method的descriptor的形式如下:

    ( ParameterDescriptor* ) ReturnDescriptor
    

    而参数的descriptor就是field的descriptor. 返回值的descriptor也是field的descriptor但是多了一个类型就是void类型, 其的descriptor如下

        VoidDescriptor:V
    

    所以举个例子, 如果一个方法的签名是

    Object m(int i, double d, Thread t) {..}
    

    那么它的descriptor就是

    (IDLjava/lang/Thread;)Ljava/lang/Object;
    
  • attribute_count的意义和field一样表示属性的个数

  • attributes[attribute_count]和field也一样表示具体的属性, 属性的个数由attribute_count决定

3. 属性

3.1 属性结构

属性这个数据结构可以出现在class文件, 字段表, 方法表中. 有些属性是特有的, 有些属性是三个共有的.

属性的描述如下:

ATTRIBUTE1
ATTRIBUTE2
ATTRIBUTE

<div style="margin-left:200px">图1-4</div>

这里我不会详细解释每一个属性, 我只解释一个对于实现mini jvm最重要的属性, Code Attribute, 为什么说它重要, 因为我们的函数的代码就是在Code Attribute中(实际上存储的是指令). 其他属性的一些解释可以参考oracle的jvm规范中的描述

3.2 Code Attribute

首先来看一下Code Attribute的结构

Code_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u2 max_stack;
    u2 max_locals;
    u4 code_length;
    u1 code[code_length];
    u2 exception_table_length;
    {   u2 start_pc;
        u2 end_pc;
        u2 handler_pc;
        u2 catch_type;
    } exception_table[exception_table_length];
    u2 attributes_count;
    attribute_info attributes[attributes_count];
}

可以看到Code Attribute属性是非常复杂的, 下面解释一些每个成员的意义.

  • attribute_name_index指向的常量池中常量项的索引, 而且这个常量项的类型必须是UTF8 Info, 值必须是"Code"
  • attribute_length表示这个属性的长度, 但是不包括开始的6个字节
  • max_stack表示Code属性所在的方法在运行时形成的函数栈帧中的操作数栈的最大深度(真是不得不佩服java编译器, 连一个函数运行时需要的操作数栈的深度都可以计算的出来)
  • max_locals表示最大局部变量表的长度
  • code_length表示Code属性所在的方法的长度(这个长度是方法代码编译成字节后字节的长度)
  • code[length]表示的就是具体的代码, 所以说java函数的代码长度是有限制的, 编译出来的字节指令的长度只能是4个字所能代表的最大值. 所以一个函数的代码不能太长, 否者是不能编译的.
  • exception_table_length表示方法会抛出的异常数量
  • exception_table[exception_table_length]表示具体的异常
  • attributes_count表示Code属性中子属性的长度, 之所以说属性复杂就是因为属性中还可以嵌套属性
  • attributes[attributes_count]代表具体的属性

现在来直观的看一下Code Attribute的组成

图1-5 CODE-ATTRIBUTE3

3.3 Code Attribute的两个子属性

Code Attribute中的两个子属性对于这次的mini jvm的实现可能不是很重要, 但是它们对于调试程序是非常重要的, 不知道大家有没有想过为什么我们用IDE运行程序出错时, IDE可以准确的定位到是哪一行代码出错了? 为什么我们在断点调试的时候可以看到每一个变量的值? 很关键的原因就在于Code属性的这两个子属性.

3.3.1 LineNumberTable

LineNumberTable的结构

LineNumberTable_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u2 line_number_table_length;
    {   u2 start_pc;
        u2 line_number; 
    } line_number_table[line_number_table_length];
}

我们着重要看的是line_number_table这个成语, 可以看到这个成员表示的就是字节码指令和源码的对应关系, 其中start_pc是Code Attribute中的code[]数组的索引值, line_number是源文件的行号

3.3.1 LocalVariableTable

LocalVariableTable的结构

LocalVariableTable_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u2 local_variable_table_length;
    {   u2 start_pc;
        u2 length;
        u2 name_index;
        u2 descriptor_index;
        u2 index;
    } local_variable_table[local_variable_table_length];
}

其中最关键的成员大家也可以想到, 肯定是local_variable_table[local_variable_table_length]

  • start_pc和length表示局部变量的索引范围([start_pc, start_pc + length))
  • name_index表示变量名在常量池中的索引
  • descriptor_index表示变量描述符在常量池中的索引
  • index表示此局部变量在局部变量表中的索引

4. 总结

至于对字段, 方法, 属性的解析的代码实现这里就不描述了, 大家直接看代码实现吧.
到这篇为止, class文件的结构和解析已经全部介绍完了, 接下来的就是运行程序了, 也就是实现一个执行引擎了, 所以接下来的才是整个jvm的重点.

5. 代码地址

6. 本系列其他文章

手把手教你撸一个Mini JVM系列(1)之解析Class File -- 初探
手把手教你撸一个Mini JVM系列(2)之解析Class File -- 常量池
手把手教你撸一个Mini JVM系列(4)之执行引擎
手把手教你撸一个Mini JVM系列(5)之源码分析 -- 常量池、访问标志、类索引
手把手教你撸一个Mini JVM系列(6)之控制流 -- 条件判断和循环

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

推荐阅读更多精彩内容