class 第一个元素_Class文件属性表-Code属性

属性表中各个属性都有固定的格式,如下图所示:

2feb4e70b18e4f1972abf229e2a46fb5.png
  • u2的属性名称索引(在常量池中的位置)
  • u4属性表内容的长度
  • u1具体的属性内容

Code属性

Code属性是整个Class文件中最重要的属性,Code属性只作用于方法表中,在Code属性中存储了Java方法体经过编译后Java的字节码指令,Code属性的结构如下:

e01904e3624aad1a68cf55954526feae.png

max_stack

操作数栈的最大深度。虚拟机运行的时候需要根据这个值来分配栈帧中的操作栈深度。

max_locals

局部变量表所需要的空间,单位是Slot,对于int、byte和returnAddress不超过32位的数据类型采用1个Slot来存储,而double和long等64位的数据类型采用两个Slot存储。

方法中的参数(实例方法还有一个隐藏的this),显示异常处理参数(try-cacatch中的异常)、方法体中的局部变量都需要用局部变量表存储。

code_length

code_length存储了字节码指令的长度,虽然长度是4个字节(表面也就是说字节码指令的长度可以达到2^32-1),但实际上Java虚拟机规定了方法体中的字节码指令最多有65535条。

code

code就存储了具体的字节码指令,具体的字节码指令我们可以不用强记,在使用的时候根据字节码去查表就可以,下图是其中一部分字节码指令:

0d462fccf7e4a2b26020efd7c45f619f.png

exception_table_length

显示异常(受检查的异常)中的个数(try-cacatch中的异常)

exception_info

这是受检查异常的具体信息,这个字段不一定存在(如果没有try-cache),下图是显示异常的具体结构:

0d83902b29ee90e727a41ca13ed2a1b8.png

当代码在start_pc和end_pc之间出现了类型为catch_type(指向常量池中一个CONSTANT_Class_info型常量的索引)的异常时,便转到handler_pc进行处理,如果catch_type为0,代表任何异常都需要转到handler_pc处进行处理。

Code属性实例分析

public class ClassTest {

    public int inc() {
        int m;
        try {
            m = 1;
            return m;
        } catch (Exception e) {
            m = 2;
            return m;
        } finally {
            m = 3;
        }
    }
}

下图是这段方法的Code属性:

a7469c8f11fd90df2abc34ecf08b7675.png

通过源码我们知道这段方法体大约有三种执行方式,一种是无任何异常返回1,另一种是有Exception及子类异常返回2,另外是抛出未受检查的异常。

下面我们站在字节码执行的角度上来看一下这段方法是如何执行的,首先我们看一下无异常的情况:

无异常执行

关于字节码的含义大家可以去查表,这里就不一一解释了,无异常执行的字节码为0-7,下面我们来详解一下7个步骤:

  • 0 iconst_1:将常量1推入操作数栈顶
  • 1 istore_1:将操作数栈顶元素(这里就是1)保存到局部变量表中的第二个Slot中,为什么会是第二个原因很简单,因为这是一个实例方法,第一个Slot为this,所以Slot的操作都是从第二个开始
  • 2 iload_1:将第二个Slot的元素(1)存放到操作数栈顶
  • 3 istore_2:将操作数栈顶元素(1)保存到局部变量表中的第三个Slot
  • 4 iconst_3:这里就是跑到了finally中的代码,将常量3推入操作数栈顶
  • 5 istore_1:将操作数栈顶元素(3)保存到局部变量表中的第二个Slot中
  • 6 iload_2: 将第三个Slot中的元素(1)放入操作数栈顶
  • 7 ireturn:返回操作数栈顶元素1

通过上述分析,我们可以看出无异常时,返回的值为1。

当发生Exception及其子类异常

首先我们通过查看受检查异常表发现,如果0-4字节码指令将会转到第8个字节码指令去执行,也就是说代码所执行的字节码指令为0-4,8-16,下面我们来分析一下当出现Exception时8-16的字节码指令:

  • 8 astore_2:将操作数栈顶引用型数据(Exception e)保存到局部变量表的第三个Slot中
  • 9 iconst_2:将常量2推入操作数栈顶
  • 10 istore_1:将操作数栈顶元素(2)保存到局部变量表的第二个Slot中
  • 11 iload_1:将第二个Slot中的元素(2)推入操作数栈顶
  • 12 istore_3:将操作数栈顶元素(2)存入局部变量表的第四个Slot中
  • 13 iconst_3:这里就是跑到了finally中的代码,将常量3推入操作数栈顶
  • 14 istore_1:将操作数栈顶元素(2)存入局部变量表的第2个Slot中
  • 15 iload_3:将第四个Slot中的元素(2)推入操作数栈顶
  • 16 ireturn:返回操作数栈顶元素2

当发生其他异常时

通过查看受检查异常表可以发现,在我们代码任何阶段发生未受检查的异常时,都会转到17个字节码指令执行,也就是说我们代码会执行17-23个字节码指令:

  • 17 astore 4:将操作数栈顶的引用型数据(其他异常)保存到局部变量表的第五个Slot
  • 19 iconst_3:这里就是跑到了finally中的代码,将常量3推入操作数栈顶
  • 20 istore_1:将栈顶元素(3)保存到局部变量表的第二个Slot中
  • 21 aload 4:将局部变量表中第5个Slot中的异常压入操作数栈顶
  • 22 athrow:将操作数栈顶异常抛出
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值