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

Posted TellH

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从字节码角度理解JVM异常处理机制的原理相关的知识,希望对你有一定的参考价值。

前几天看到一条阿里巴巴的面试题:你了解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 异常的处理原理

以上是关于从字节码角度理解JVM异常处理机制的原理的主要内容,如果未能解决你的问题,请参考以下文章

jvm原理(30)通过字节码分析this关键字以及异常表的重要作用&通过字节码分析Java异常处理机制

耗时一周深入理解JVM虚拟机异常处理字节码性能优化,全网最全面的JVM原理通俗易懂(强烈建议收藏)

深入理解 Java 虚拟机之学习笔记

JVM加载class文件的原理机制

从JVM异常表和字节码角度分析try-catch-finally为什么效率低

深入理解JVM