从字节码角度理解JVM异常处理机制的原理

来源:互联网 发布:济南婚纱摄影 知乎 编辑:程序博客网 时间:2024/06/08 05:52

前几天看到一条阿里巴巴的面试题:你了解try-catch块的实现原理吗?
我服,阿里的面试题就是有深度啊。要答好这一题,我觉得需要反编译一下.java源文件,因为字节码面前了无秘密。

反编译一段小程序

为了简单起见,我搞了一段没什么实际意义的try-catch示例代码。

public class Main {    public static void main(String[] args) {        int a = 1;        int b = 3;        try {            b = a + b;            return;        } catch (RuntimeException e1) {            b = a - b;        } catch (Exception e2) {            b = a * b;        } finally {            a = 0;        }    }}

通过命令javap -c Main 反编译.class文件得到一下输出:

  public static void main(java.lang.String[]);    Code:       0: iconst_1       1: istore_1 // 给局部变量表1号slot赋值为1,代表a       2: iconst_3       3: istore_2 // 给局部变量表2号slot赋值为3,代表b       4: iload_1  // try块开始!       5: iload_2       6: iadd       7: istore_2 // 指令4-7 完成了操作:b = a + b       8: iconst_0 // finally块开始!       9: istore_1 // 指令8-9 完成操作a=0      10: return // 函数执行完毕,返回      11: astore_3 // RuntimeException e1异常处理块开始!      12: iload_1      13: iload_2      14: isub      15: istore_2      16: iconst_0 // finally处理块      17: istore_1      18: goto          38 // RuntimeException e1异常处理块结束!      21: astore_3 // Exception e2异常处理块开始!      22: iload_1      23: iload_2      24: imul      25: istore_2      26: iconst_0 // finally处理块      27: istore_1      28: goto          38 // Exception e2 异常处理块结束!      31: astore        4 // 其他任何异常处理块      33: iconst_0      34: istore_1      35: aload         4      37: athrow // 往将异常外抛      38: return    Exception table: // 异常表       from    to  target type           4     8    11   Class java/lang/RuntimeException //4-8 号指令中,碰到 NullPointerException时,跳到 11 号指令           4     8    21   Class java/lang/Exception           4     8    31   any          11    16    31   any          21    26    31   any          31    33    31   any}

以上指令的注释是我加上去的,请大家对照源码加以理解。

总结

如果大家看完之后仍然有点模糊,相信看了下图后会对try-catch代码对应的字节码指令结构有一个全面的理解。
这里写图片描述

try块的范围就是体现在异常表行记录的起点和终点。JVM 在 try 住的代码区间内如有异常抛出的话,就会在当前栈桢的异常表中,找到匹配类型的异常记录的入口指令号,然后跳到该指令处执行。异常指令块执行完后,再回来继续执行后面的代码。JVM 按照每个入口在表中出现的顺序进行检索,如果没有发现匹配的项,JVM 将当前栈帧从栈中弹出,再次抛出同样的异常。当 JVM 弹出当前栈帧时,JVM 马上终止当前方法的执行,并且返回到调用本方法的方法中,但是并非继续正常执行该方法,而是在该方法中抛出同样的异常,这就使得 JVM 在该方法中再次执行同样的搜寻异常表的操作。

参考:JVM 对 Java 异常的处理原理

0 0