JavaAPI首页投稿(暂停使用,暂停投稿)Android知识

手把手教你撸一个Mini JVM系列(3)之解析Class Fi

2017-06-26  本文已影响186人  CoderMonkey

引子:

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

1. 字段

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

先来看一下字段的结构

    Field_Info {
        u2 access_flag;
        u2 name_index;
        u2 descriptor_index;
        u2 attribute_count;
        attribute_info 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]
    }

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

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属性是非常复杂的, 下面解释一些每个成员的意义.

现在来直观的看一下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]

4. 总结

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

5. 代码地址

6. 本系列其他文章

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

上一篇下一篇

猜你喜欢

热点阅读