環境:HotSpot jdk8
>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源代碼
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反彙編
javap -v -l -p _01_ExceptionBytecode.class
反彙編後的字節碼解讀
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