一段处理异常的java代码的字节码解释

2023-05-05 19:46:37 浏览数 (1)

环境:HotSpot jdk8

代码语言:javascript复制
>java -version
java version "1.8.0_211"
Java(TM) SE Runtime Environment (build 1.8.0_211-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.211-b12, mixed mode)

java源代码

代码语言:javascript复制
public class _01_ExceptionBytecode {

    private int exec1(){
        int i;
        try {
            i =  exec2();
            return i;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            return -1;
        }
    }

    private int exec2() throws Exception{
        return 2;
    }
}

使用javap反汇编

代码语言:javascript复制
javap -v -l -p _01_ExceptionBytecode.class

反汇编后的字节码解读

代码语言:javascript复制
public org.byron4j.jvm._01_ExceptionBytecode();
  // .... 省略类的一些信息,直接看方法的Code部分  
  private int exec1();
    Code:
         0: aload_0 						  // aload_0: 将第一个引用类型本地变量推送至栈顶
         									  //  因为exec1是实例方法,则首个局部变量为this;
         1: invokespecial #2                  // invokespecial : 调用超类构建方法, 实例初始化方法, 私有方法;  这里调用的是exec2方法
         4: istore_1						  // istore_1: 将栈顶 int 型数值存入第二个本地变量; 这里是i。  
         5: iload_1							  // iload_1:  将第二个 int 型本地变量推送至操作数栈栈顶;   i =  exec2()
         6: istore_2						  // istore_2: 将栈顶 int 型数值存入第三个本地变量  ----这里是返回值  return i  
         7: iconst_m1						  // 这里是开始finally块代码:  将 int 型-1 推送至栈顶;  
         8: ireturn							   //这里是开始finally块代码:  return -1;
         9: astore_2						  // 这里是catch代码: 将栈顶引用型数值存入第三个本地变量
        10: aload_2							  //  这里是catch代码:将第三个引用类型本地变量推送至栈顶   ;  Exception e
        11: invokevirtual #4                  // Method java/lang/Exception.prin    ;  调用  e.printStackTrace();
tStackTrace:()V
        14: iconst_m1                        //  这里是开始finally块代码:  将 int 型-1 推送至栈顶;  
        15: ireturn							 //  这里是开始finally块代码:  return -1;
        16: astore_3						 //  保存结果
        17: iconst_m1                        //  (真正的finally)这里是开始finally块代码:  将 int 型-1 推送至栈顶;  
        18: ireturn                          //  (真正的finally)这里是开始finally块代码:  return -1;
      Exception table:                       // 异常表
         from    to  target type
             0     7     9   Class java/lang/Exception         //  0 到  7 可能出现的异常,会被目标9捕获java.langException类型
             0     7    16   any                               //  0 到  7 可能出现异常,在16可能捕获或者抛出any类型异常(错误)
             9    14    16   any                               //  9 到  14 可能出现异常,在16可能捕获或者抛出any类型异常(错误)
      // 栈帧信息省略...

  private int exec2() throws java.lang.Exception;
    Code:
         0: iconst_2              //  将 int 型 2 推送至栈顶
         1: ireturn               // 从当前方法返回 int   ;  return 2;
    Exceptions:
      throws java.lang.Exception

这里我们来解释一下:

从JDK8开始,字节码处理finally时变为通过冗余finally代码块来解决。(jdk7以及之前可以通过jsr跳转指令处理。)

  • 如果程序正常执行:
    • exec1会执行0、1、4、5、6、7、8; 这里的7、8是冗余(可以认为拷贝了一份)finally代码块——这是因为要保证finally必须执行的要求。
    • 在执行finally代码块之前会先保存try代码块的返回值; 但是最终被finally的return覆盖。
  • 如果出现了调用exec2出现了Exception,则会执行14、15、16,在catch里面也会冗余finall代码块的内容——这也是因为要保证finally必须执行的要求。

总结: 通过理解字节码反汇编后的JVM指令,可以加深对java代码执行过程的理解; 其他的任意java代码都可以使用前面的javap指令查看。

JVM指令集中文本

这里分享一份JVM指令集的中文解释版本PDF

链接:https://pan.baidu.com/s/1OAO7R7NRCqIFbkSCiOqaJQ 提取码:zimg

0 人点赞