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] 属性实际的信息

常用的属性见下表,全部的属性字段见 Table 4.7-A. attributes

属性名称 使用位置 说明
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,这些指令到底表示什么呢?

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 属性表的结构如下,更多参考 JVM 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 属性表的结构如下,更多参考 JVM 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,这些指令到底表示什么呢?

  • 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 参数。

参考:

  1. 周志明,深入理解Java虚拟机 - 第 6 章:类文件结构
  2. jclasslib IDEA 中使用方法

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

JVM 字节码(二)方法表详解的更多相关文章

  1. Java之字节码(2) - .class文件格式详解

    转载来自 小介:去 年在读<深入解析JVM>的时候写的,记得当时还想着用自己的代码解析字节码的,最后只完成了一部分.现在都不知道还有没有保留着,貌似Apache有现 成的BCEL工程可以做 ...

  2. jvm 字节码执行 (一)方法调用

    “虚拟机”是一个相对于“物理机”的概念,这两种机器都有代码执行能力,其区别是物理机的执行引擎是直接建立在处理器.硬件.指令集和操作系统层面上,而虚拟机的执行引擎是 由自己实现的,因此可以自行制定指令集 ...

  3. 转:JAVAWEB开发之权限管理(二)——shiro入门详解以及使用方法、shiro认证与shiro授权

    原文地址:JAVAWEB开发之权限管理(二)——shiro入门详解以及使用方法.shiro认证与shiro授权 以下是部分内容,具体见原文. shiro介绍 什么是shiro shiro是Apache ...

  4. 我的书籍《深入解析Java编译器:源码剖析与实例详解》就要出版了

    一个十足的技术迷,2013年毕业,做过ERP.游戏.计算广告,在大公司呆过,但终究不满足仅对技术的应用,在2018年末离开了公司,全职写了一本书<深入解析Java编译器:源码剖析与实例详解> ...

  5. JVM 字节码(四)静态方法、构造代码、this 以及 synchronized 关键字

    JVM 字节码(四)静态方法.构造代码.this 以及 synchronized 关键字 一.静态代码 public class ByteCodeStatic { private static fin ...

  6. JVM 字节码(三)异常在字节码中的处理(catch 和 throws)

    JVM 字节码(三)异常在字节码中的处理(catch 和 throws) 在 ClassFile 中到底是如何处理异常的呢? 一.代码块异常 catch catch 中的异常代码块在异常是如何处理的呢 ...

  7. JVM 字节码(一)字节码规范

    JVM 字节码(一)字节码规范 JVM 学习资源 Java ClassFile 字节码规范(Oracle) Java 虚拟机规范(Java SE 7 中文版) (周志明等译) Java 反编译工具 - ...

  8. MySQL慢查询(二) - pt-query-digest详解慢查询日志 pt-query-digest 慢日志分析

    随笔 - 66 文章 - 0 评论 - 19 MySQL慢查询(二) - pt-query-digest详解慢查询日志 一.简介 pt-query-digest是用于分析mysql慢查询的一个工具,它 ...

  9. JVM 字节码指令手册 - 查看 Java 字节码

    JVM 字节码指令手册 - 查看 Java 字节码 jdk 进行的编译生成的 .class 是 16 进制数据文件,不利于学习分析.通过下命令 javap -c Demo.class > Dem ...

随机推荐

  1. ubuntu-docker入门到放弃(八)创建支持SSH服务的镜像

    我们知道进入docker容器可以使用attach.exec等命令来操作和管理,但是如果需要远程登录并管理容器,就需要ssh服务的支持了. 1.基于commit命令创建 docker提供了commit命 ...

  2. JavaScript常见的代码精简

    1.&& callback && callback() 等价于: if(callback){ callback(); } 表达的意思: 先判断 callback 是不是 ...

  3. Java String对象的问题 String s="a"+"b"+"c"+"d"

    1, String s="a"+"b"+"c"+"d"创建了几个对象(假设之前串池是空的) 2,StringBuilde ...

  4. PAT 乙级1093 字符串A+B (20 分)

    1093 字符串A+B (20 分) 给定两个字符串 A 和 B,本题要求你输出 A+B,即两个字符串的并集.要求先输出 A,再输出 B,但重复的字符必须被剔除. 输入格式: 输入在两行中分别给出 A ...

  5. Linux操作redis 使用(VMwareWorkstation)

    项目一般都部署到linux上面,记得刚出来的时候,第一家公司 服务器是windows系统,以后公司的项目都放在了linux上面,所以掌握linux的一些基本操作是一个程序员必备的知识,本次记录如何使用 ...

  6. uva-10245-分治

    题意:数组二维空间内的点,求最近的俩个点的距离. 根据x排序,求左部分的最近距离,右部分最近距离,然后以中点,当前距离为半径,计算所有的点距离. #include <string> #in ...

  7. 面试回顾——List<T>排序

    1.如何对List<T>排序: public static void main(String[] args) { Student stu1=new Student("张三&quo ...

  8. 关于PS的操作

    1.移动工具 Ctrl+J:拷贝图层 Ctrl+T:自由变换调整大小 Alt+Shift:全选 Ctrl+G:图层编组 Alt+Delete:选中图层填充当前颜色 Ctrl+Alt+Z:后退一步 2. ...

  9. Shell脚本1-20例

    1.每天生成一个文件 描述:请按照这样的日期格式(xxxx-xx-xx)每日生成一个文件,例如今天生成的文件为)2017-07-05.log, 并且把磁盘的使用情况写到到这个文件中,(不用考虑cron ...

  10. leetcode494

    public class Solution { public int FindTargetSumWays(int[] nums, int S) { Queue<int> Q = new Q ...