环境: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