java 0x2a_JVM 字节码(二)方法表详解

JVM 字节码(二)方法表和属性表

上一节中对 ClassFile 的整体进行了五个详细的说明, 本节围绕 ClassFile 最重要的一个内容 - 方法表的 Code 属性展开 ,更多 JVM Methods 规范(Oracle)

一、方法表结构

1.1 方法表结构

方法表的结构如下:

method_info {

u2 access_flags;

u2 name_index;

u2 descriptor_index;

u2 attributes_count;

attribute_info attributes[attributes_count];

}

其中属性表 attribute_info 是最复杂的结构,其中就包含指令集 Code 属性。结构如下:

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];

}

1.2 属性表结构

在 JVM 规范中所有属性都有以下几个字段:

attribute_info {

u2 attribute_name_index;

u4 attribute_length;

u1 info[attribute_length];

}

attribute_name_index 属性名称,指向常量池

attribute_length 属性实际长度,不包含 attribute_name_index 和 attribute_length 的长度,也就是说实际长度 = 2 + 4 + attribute_length

info[attribute_length] 属性实际的信息

属性名称

使用位置

说明

ConstantValue

字段表

final 关键字定义的常量值

Code

方法表

Java 代码编译的字节码指令

LineNumberTable

Code 属性

Java 源码的行号与字节码指令的对应关系

LocalVariableTable

Code 属性

方法的局部变量描述

Exceptions

方法表

方法抛出的异常

SourceFile

类文件

源文件名称

InnerClasses

类文件

内部类列表

Synthetic

类、方法表、属性表

标识方法或字段为编译器自动生成的

二、案例分析

还是使用上一个例子,方法表有两个方法

2.1 init 方法分析

init 是 javac 自动生成的方法,对应的字节码如下:

Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F

00000120 00 01 00 07 00 08 00 01

00000130 00 09 00 00 00 2F 00 01 00 01 00 00 00 05 2A B7

00000140 00 01 B1 00 00 00 02 00 0A 00 00 00 06 00 01 00

00000150 00 00 07 00 0B 00 00 00 0C 00 01 00 00 00 05 00

00000160 0C 00 0D 00 00

前 8 个字节 00 01 00 07 00 08 00 01 就不分析了,重点关注 init 方法的 Code 属性如下:

attribute_name_index 属性名称,指向常量 #0009,即 Code

attribute_length 属性长度,不包括这两个属性的长度,本例中为 00 00 00 2F,共 47 + [2(attribute_name_index) + 4(attribute_length)] 个字节

max_stack 栈深度,本例中为0001

max_locals 局部变量表,本例中为0001

code_length code 长度,本例中为 0000 0005,即 5 个字节

code[code_length] 指令集,本例中为 2A B7 00 01 B1

exception_table_length 异常长度,本例中为 00 00

attributes_count Code 的子属性表,本例中为 00 02,包含 LineNumberTable 和 LocalVariableTable 两个属性。LineNumberTable 是指令集对应的源码行号,主要用于异常时抛出对应的源码行号,便于定位问题。LocalVariableTable 是局部方法表。

接下来我们主要关注两个区域,指令集(code) 和属性表(attribute_info)

2.1.1 指令集(code)

init 的指令集为 2A B7 00 01 B1,这些指令到底表示什么呢?

4ad1ccbfc7dcdec2c2ba23225f2c0b51.png

IDEA 安装插件后有一个好处,可以点击指令后直接跳转到对应的 Oracle 官方规范上,十分方便。

aload_0 对应 0x2a,用于加载局部方法表中的参数到操作数栈中。

invokespecial 对应 0xb7,后两个字节用于指向常量池中的类型 #1。只能调用三类方法:方法;private方法;super.method()。因为这三类方法的调用对象在编译时就可以确定。更多关于 invokespecial 指令

return 对应 0xb1。

2.1.2 属性表(attribute_info)

0002 表示有两个属性,本例中为 LineNumberTable 和 LocalVariableTable,下面来认识一下这两个属性。

(1) 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];

}

对应的字节码如下:

Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F

00000140 00 0A 00 00 00 06 00 01 00

00000150 00 00 07

attribute_name_index 指向常量池 #0A,即 LineNumberTable

attribute_length 属性长度,本例中为 00 00 00 06,实际属性值为 00 01 00 00 00 07

line_number_table_length 长度,本例中为 00 01,即只有一条记录

line_number_table[line_number_table_length] 行号对应的记录,本例中为 0000 -> 0007

(2) 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];

}

对应的字节码如下:

Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F

00000150 00 0B 00 00 00 0C 00 01 00 00 00 05 00

00000160 0C 00 0D 00 00

attribute_name_index 指向常量池 #0B,即 LocalVariableTable

attribute_length 属性长度,本例中为 00 00 00 0C(12 个字节),实际属性值为 00 01 00 00 00 05 00 0C 00 0D 00 00

local_variable_table_length 长度,本例中为 00 01,即只有一条记录

local_variable_table[local_variable_table_length] 局部变量表,本例中为 00 00 00 05 00 0C 00 0D 00 00,其中 #0D 正是指向 this,所以非静态方法中都有一个默认的 this 参数。

2.1 inc 方法分析

inc 是自定义的方法,对应的字节码如下:

Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F

00000160 00 02 00 0E 00 0F 00 01 00 09 00

00000170 00 00 31 00 02 00 01 00 00 00 07 2A B4 00 02 04

00000180 60 AC 00 00 00 02 00 0A 00 00 00 06 00 01 00 00

00000190 00 0C 00 0B 00 00 00 0C 00 01 00 00 00 07 00 0C

000001A0 00 0D 00 00

access_flags 访问类型,0002

name_index 方法名称,000E

descriptor_index 方法参数及返回值类型,#000F

attributes 方法属性,0001,表示有一个方法属性,即 Code

inc 方法的 Code 属性解析如下:

attribute_name_index 属性名称,指向常量 #0009,即 Code

attribute_length 属性长度,不包括这两个属性的长度,即 00 00 00 31,共 49 + [2(attribute_name_index) + 4(attribute_length)] 个字节

max_stack 栈深度,0002

max_locals 局部变量表,0001

code_length code 长度,0000 0007,即 7 个字节

code[code_length] 2A B4 00 02 04 60 AC 表示对应的指令

2.2.1 指令集(code)

init 的指令集为 2A B4 00 02 04 60 AC,这些指令到底表示什么呢?

7d3bac79d90fb52480c53ed94e2f7e34.png

aload_0 对应 0x2a,用于加载局部方法表中的参数到操作数栈中。

getfield 对应 0xb4,后两个字节用于指向常量池中的类型 #2

iconst_1 对应 0x04,将常量 1 压栈

iadd 对应 0x60。

ireturn 对应 0xac。

2.2.2 属性表(attribute_info)

0002 表示有两个属性,本例中也是 LineNumberTable 和 LocalVariableTable 两个属性。

(1) LineNumberTable

Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F

00000180 00 0A 00 00 00 06 00 01 00 00

00000190 00 0C

attribute_name_index 指向常量池 #0A,即 LineNumberTable

attribute_length 属性长度,本例中为 00 00 00 06,实际属性值为 00 01 00 00 00 0C

line_number_table_length 长度,本例中为 00 01,即只有一条记录

line_number_table[line_number_table_length] 行号对应的记录,本例中为 0000 -> 000C

(2) LocalVariableTable

Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F

00000190 00 0B 00 00 00 0C 00 01 00 00 00 07 00 0C

000001A0 00 0D 00 00

attribute_name_index 指向常量池 #0B,即 LocalVariableTable

attribute_length 属性长度,本例中为 00 00 00 0C(12 个字节),实际属性值为 00 01 00 00 00 05 00 0C 00 0D 00 00

local_variable_table_length 长度,本例中为 00 01,即只有一条记录

local_variable_table[local_variable_table_length] 局部变量表,本例中为 00 01 00 00 00 07 00 0C 00 0D 00 00,其中 #0D 正是指向 this,所以非静态方法中都有一个默认的 this 参数。

参考:

周志明,深入理解Java虚拟机 - 第 6 章:类文件结构

每天用心记录一点点。内容也许不重要,但习惯很重要!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值