Java異常詳解-從字節碼角度查看異常實現原理

一、Java異常處理機制

Java異常的定義、類型和用法在網上有很多優秀的文章,這裏直接引用一篇寫得比較詳細的文章。
深入理解Java異常處理機制

一個簡單的異常繼承樹
在這裏插入圖片描述

接下來做一個知識點的提出,如果有哪一個點不懂,可以查看上面引用的文章進行解惑。

  • Java異常的繼承樹

    • 頂層父類:Throwable
    • 兩個重要子類:Error(錯誤)Exception(異常)
  • 異常

    • 可查異常不可查異常定義
    • 運行時異常非運行時異常定義
  • try - catch - finally

    • 處理順序
  • 處理異常機制

    • 拋出異常
    • 捕獲異常
    • 異常鏈
    • 拋出異常時,異常覆蓋問題

二、異常在字節碼層面的實現

2.1 一個簡單的例子

2.1.1 例子的源碼

public class Exception1{
	public static void main(String[] args){
		int a,b,c,d;
		try{
			 a =1 ;
			
		}catch(Exception e){
			 b = 2;
		}finally{
			 c = 3 ;
		}
		 d = 4;
	}
}

2.1.2 查看字節碼並分析方法執行過程

這裏只放出main函數的的部分

                                                                                     
 public static void main(java.lang.String[]);                                        
   descriptor: ([Ljava/lang/String;)V                                                
   flags: ACC_PUBLIC, ACC_STATIC                                                     
   Code:                                                                             
     stack=1, locals=7, args_size=1                                                  
        0: iconst_1               // 將int型常量1入棧                                                            
        1: istore_1               // 將棧頂int型的數賦予第二個局部變量(即將常量1賦值到局部變量a)                                                     
        2: iconst_3               // 將int型常量3入棧                                                          
        3: istore_3               // 將棧頂的數賦予第四個局部變量(即將常量3賦值到局部變量c)                                                   
        4: goto          23       // 跳轉到偏移量爲21的指令                                                   
        7: astore        5        // 棧頂ref對象存入第1局部變量                                                  
        9: iconst_2               // 將int型常量2入棧                                                       
       10: istore_2               // 將棧頂的數賦予第三個局部變量(即將常量3賦值到局部變量b)                                                   
       11: iconst_3               // 將int型常量3入棧                                                      
       12: istore_3               // 將棧頂的數賦予第四個局部變量(即將常量3賦值到局部變量c)                                                   
       13: goto          23                                                          
       16: astore        6                                                           
       18: iconst_3                                                                  
       19: istore_3                                                                  
       20: aload         6                                                           
       22: athrow                                                                    
       23: iconst_4                                                                  
       24: istore        4                                                           
       26: return                                                                    
     Exception table:                                                                
        from    to  target type                                                      
            0     2     7   Class java/lang/Exception                                
            0     2    16   any                                                      
            7    11    16   any                                                      
           16    18    16   any                                                      
                           

上面方法的執行,將a變量賦值之後會將c變量也進行賦值,將b變量賦值也會對c變量進行賦值,從這裏我們可以知道finally實現的原理: 編譯器會將finally的代碼塊全部複製一遍貼到正常代碼的下面,這樣就保證了finally的代碼塊總會被執行

2.1.3 異常處理過程

在上述字節碼中,有一個表是異常處理時用到的表:Exception Table,它裏面包含了異常處理開始的偏移量、結束偏移量、異常捕捉的類型等等。下面進行詳細的介紹:

  • Exception table: 異常處理信息表
  • from 異常處理開始的位置
  • to 異常處理結束的位置
  • fromto結合起來就是異常處理的位置。在上述例子中,Exception table的第一行fromto分別表示爲0,2,這裏代表着異常是從0開始,到2結束(不包括2),表示的是try包含的代碼塊。
  • target 異常處理器的起始位置,即catch開始處理的位置
  • type 異常類型,any表示所有類型

異常處理的過程

當程序觸發異常時,Java虛擬機會遍歷異常表中的所有條目(即try裏面的所有代碼)。如果異常找到異常發生的字節碼條目,則會跟catch要捕捉的異常匹配,如果匹配,則開始執行catch裏面的代碼。
如果沒有匹配到,那麼它會彈出當前方法對應的 Java 棧幀,並且在調用者(caller)中重複上述操作。在最壞情況下,Java 虛擬機需要遍歷當前線程 Java 棧上所有方法的異常表
如果在catch中發生了異常,那麼Java虛擬機會拋棄第一個異常,嘗試捕獲並處理新的異常。這個在編碼中是很不利於調試的。

總結

  • Java異常處理機制的瞭解
  • finally總會被執行的原理
  • 在JVM層面的異常處理過程
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章