一段處理異常的java代碼的字節碼解釋

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章