从字节码看JVM处理异常及finally模块

JVM异常

在java中,异常分为显示异常和隐式异常两种。显示异常指java应用抛出的异常,程序遇到了无法执行的逻辑,通过throw抛出异常;隐式异常指虚拟机在执行过程中,碰到错误无法执行下去,不得不停止执行,自动抛出异常,比如对象执行方法,结果该对象为Null,那么会出现NPE。

关于异常的内容

异常篇

为什么不能通过异常来处理业务逻辑

异常实例的构建过程十分复杂。在某个方法中生成了异常,一般我们会得到这个异常的栈轨迹,这个内容就是由JVM来生成的。JVM会从当前出现异常的方法开始,逐一向下访问所在线程的栈帧,并且记录下各种异常相关的信息,异常所在的类、方法、行。

JVM是如何捕捉异常的

在java文件编译后生成的字节码文件中,每个方法都带有一个异常表,异常表中含有from、to、target三种指针及不活的异常的类型,如果没有try、catch模块,那么捕获的异常类型为any。
当程序触发异常时,Java 虚拟机会从上至下遍历异常表,检查当前异常触发的索引是否在异常表中某个异常的监控范围内,如果在范围内,那么jvm会判断所抛出的异常与异常表查询的异常类型是否匹配,匹配的话那么跳转到该条异常所指定的字节。
如果遍历完了异常表没有查询到对应的异常,那么会弹出栈帧,将异常抛出到该方法的调用者,继续执行这个流程。

JVM中的finally

finally模块在java语言中是必须执行的,在编译的时候,通过将finally中代码块分别在try模块的最后和catch模块的最后都复制了一份,通过这样来保证finally的必定执行。如下面代码及class文件内容。

public class TestException {

    private int tryValue;
    private int catchValue;
    private int finalValue;

    public void test() {
        try {
            tryValue = 1;
        } catch (Exception e) {
            catchValue = 2;
        } finally {
            finalValue = 3;
        }
    }

    /**
     * 在索引为5和索引为19的位置可以看到,代码里并没有的为finalValue赋值的内容在这里出现了,
     * 这是在编译的时候,将finally模块的内容添加过来保证finally的执行,执行结束后直接跳转到return.
     * 那么在索引27的位置,依旧出现了finally的模块内容,这是因为可能在catch模块出现异常,
     * 这个finally模块的内容是为了保证catch模块里异常后必定被执行(可看下面异常表)
     *  public void test();
     *     descriptor: ()V
     *     flags: ACC_PUBLIC
     *     Code:
     *       stack=2, locals=3, args_size=1
     *          0: aload_0
     *          1: iconst_1
     *          2: putfield      #2                  // Field tryValue:I
     *          5: aload_0
     *          6: iconst_3
     *          7: putfield      #3                  // Field finalValue:I
     *         10: goto          35
     *         13: astore_1
     *         14: aload_0
     *         15: iconst_2
     *         16: putfield      #5                  // Field catchValue:I
     *         19: aload_0
     *         20: iconst_3
     *         21: putfield      #3                  // Field finalValue:I
     *         24: goto          35
     *         27: astore_2
     *         28: aload_0
     *         29: iconst_3
     *         30: putfield      #3                  // Field finalValue:I
     *         33: aload_2
     *         34: athrow
     *         35: return
     *       // 方法中的异常表
     *       Exception table:
     *          from    to  target type
     *              0     5    13   Class java/lang/Exception
     *              0     5    27   any
     *             13    19    27   any
     */
}

finally中return覆盖try中返回值

从字节码的角度来看finally中return是如何覆盖try中return的

public class TestException {

    private int tryValue;
    private int catchValue;
    private int finalValue;

    public int test() {
        try {
            tryValue = 1;
            return tryValue;
        } catch (Exception e) {
            catchValue = 2;
        } finally {
            finalValue = 3;
        }
        return finalValue;
    }

    /**
     * return在finally外版本
     * public int test();
     *     descriptor: ()I
     *     flags: ACC_PUBLIC
     *     Code:
     *       stack=2, locals=3, args_size=1
     *          0: aload_0
     *          1: iconst_1
     *          2: putfield      #2                  // Field tryValue:I
     *          5: aload_0
     *          6: getfield      #2                  // Field tryValue:I
     *          9: istore_1      // 将tryValue写回return变量中
     *         10: aload_0
     *         11: iconst_3
     *         12: putfield      #3                  // Field finalValue:I  // 为finalValue赋值
     *         15: iload_1      // 取return变量写入栈顶
     *         16: ireturn      // 返回栈顶变量
     *         17: astore_1
     *         18: aload_0
     *         19: iconst_2
     *         20: putfield      #5                  // Field catchValue:I
     *         23: aload_0
     *         24: iconst_3
     *         25: putfield      #3                  // Field finalValue:I
     *         28: goto          39
     *         31: astore_2
     *         32: aload_0
     *         33: iconst_3
     *         34: putfield      #3                  // Field finalValue:I
     *         37: aload_2
     *         38: athrow
     *         39: aload_0
     *         40: getfield      #3                  // Field finalValue:I  // 取对象中finalValue
     *         43: ireturn          // 返回栈顶变量
     *       Exception table:
     *          from    to  target type
     *              0    10    17   Class java/lang/Exception
     *              0    10    31   any
     *             17    23    31   any
     */
}

public class TestException {
    
    private int tryValue;
    private int catchValue;
    private int finalValue;

    public int test() {
        try {
            tryValue = 1;
            return tryValue;
        } catch (Exception e) {
            catchValue = 2;
        } finally {
            finalValue = 3;
            return finalValue;
        }
    }
    /**
     * return在finally模块里版本
     * 最大的变化在索引12之后,获取到了对象中的finalValue,写入栈顶,然后返回,这里相当于覆盖了return变量里的值,因此try中的return失效了。
     * public int test();
     *     descriptor: ()I
     *     flags: ACC_PUBLIC
     *     Code:
     *       stack=2, locals=3, args_size=1
     *          0: aload_0
     *          1: iconst_1
     *          2: putfield      #2                  // Field tryValue:I
     *          5: aload_0
     *          6: getfield      #2                  // Field tryValue:I
     *          9: istore_1
     *         10: aload_0
     *         11: iconst_3
     *         12: putfield      #3                  // Field finalValue:I
     *         15: aload_0
     *         16: getfield      #3                  // Field finalValue:I
     *         19: ireturn
     *         20: astore_1
     *         21: aload_0
     *         22: iconst_2
     *         23: putfield      #5                  // Field catchValue:I
     *         26: aload_0
     *         27: iconst_3
     *         28: putfield      #3                  // Field finalValue:I
     *         31: aload_0
     *         32: getfield      #3                  // Field finalValue:I
     *         35: ireturn
     *         36: astore_2
     *         37: aload_0
     *         38: iconst_3
     *         39: putfield      #3                  // Field finalValue:I
     *         42: aload_0
     *         43: getfield      #3                  // Field finalValue:I
     *         46: ireturn
     *       Exception table:
     *          from    to  target type
     *              0    10    20   Class java/lang/Exception
     *              0    10    36   any
     *             20    26    36   any
     */
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值