引子
首先,小編聲明一下,這里講的Exception不僅僅是Exception一個類,而是異常機制,就像下面圖中的所有(含繼承Error和Exception的)。
異常類圖
當然JDK中更為細致的異常繼承體系也不是本篇探討內容,本次呢,指北君將對異常在JVM層面的執行原理和ARM進行介紹,這里ARM也不是芯片架構,而是指自動資源管理。
異常表
要想了解異常執行原理,異常表是一個最佳的入口,異常表是什么?我來看看下面的數據結構描述:
exception_table {
u2 start_pc;
u2 end_pc;
u2 handler_pc;
u2 catch_type;
}
它是虛擬機中用于表述異常處理的一種數據結構,我再來看異常表的樣例,下面是一段包含try-catch代碼編譯后的字節碼中的內容:
from to target type
8 41 41 Class java/lang/IllegalArgumentException
8 41 41 Class java/io/IOException
8 53 63 any
邏輯上的表述可以用下圖表示:
異常類圖
- 監測從8-41(41不包含)號指令的IllegalArgumentException
- 監測從8-41指令的IOException
- 監測從8到53指令的結束動作,包含三種情況:跳出指令(比如return指令集);athrow指令且沒有匹配的異常類型;指令執行到最后一條
- 如果監測到指定動作,則按照異常表進行跳轉
現在,回到異常表,我們可以看到異常表包含多條異常處理數據,每條數據包含四個屬性
- from 監控的指令集起始編號
- to 監控的指令集結束編號
- target 滿足條件后指令跳轉目標
- 異常匹配的類型,特殊類型any,對應fanally塊,監控try塊和catch塊
對于return,athrow等指令,執行順序需要注意,指令是在對應的處理之后執行。
可能會讓你意識混亂的例子:
public String execOrder(int sn) {
StringBuilder build = new StringBuilder();
try {
build.append("try-block");
if(sn == 0) {
return build.append(", end").toString();
}
}catch(RuntimeException e) {
build.append(", catch-block");
}finally{
build.append(", finally-block");
}
return build.toString();
}
我們之前說過finally塊會在return之前執行,那是不是執行結果是:try->finally->end這種順序呢?實際的執行的結果是:try-block,end。可以看到,finally塊沒有對結果形成影響,當然,這并不說它沒執行,只是reutrn的結果在執行finally塊時已經計算出結果了,在執行完finally塊后將之前計算的結果返回了而已。所以我們要深入理解了執行的原理,才能正確理解結果。
ARM 自動資源管理
講完前面的異常執行原理后,指北君現在給大家介紹異常機制在JDK1.7中一個優化特性:自動資源管理。經常使用IO的小伙伴一定對關閉IO很煩,寫法繁瑣,關閉前還要做判斷,并且在關閉塊代碼還要加try-catch,就如同下面類似的代碼:
InputStreamReader in = null;
OutputStreamWriter out = null;
try {
in = new InputStreamReader(new FileInputStream(""));
}catch(IOException e) {
}finally {
try {
if(in != null) {
in.close();
}
if(out != null) {
in.close();
}
}catch(IOException e) {
}
}
反正指北君每次寫這種代碼的時候很煩,就是那種啥事沒干,占我一塊黃金代碼位置的感覺,對于有精簡代碼癖好的人來說,就像吃了一口蒼蠅般難受。有了ARM后,我們再來看看新的寫法:
public void sample2() {
try (InputStreamReader in = new InputStreamReader(new FileInputStream(""))){
in.read();
}catch(IOException e) {
}finally {
}
}
是不是簡潔多了,資源直接在try后的括號內進行聲明,而且不需要顯式的關閉流的代碼,這難道就是ARM!是不是有點小興奮!對于ARM自動資源管理要點如下:
- 在try后的括號內聲明需要自動關閉的資源
- 資源必須實現一個關鍵接口:AutoCloseable。在滿足上面的條件后,JDK將為我們自動關閉資源。當然這種寫法即使想手動關閉,在catch和finally部分也拿不到資源引用。
又到了追根溯源環節了,自動資源管理是如何實現的呢?先看看class文件有啥變化。
21: aload_3
22: invokevirtual #112 // Method java/io/InputStreamReader.read:()I
25: pop
26: ldc #51 // String try-block
28: astore 4
30: aload_3
31: ifnull 94
34: aload_3
35: invokevirtual #103 // Method java/io/InputStreamReader.close:()V
38: goto 94
41: astore_1
42: aload_3
43: ifnull 50
46: aload_3
47: invokevirtual #103 // Method java/io/InputStreamReader.close:()V
50: aload_1
51: athrow
52: astore_2
對比之前的字節碼文件,我們明顯發現雖然代碼行數少了,但是Class編譯后的內容一點都沒少,而且異常表中除了finally對應的處理,還增加了額外的部分。具體幾個不同點:
- 字節碼中竟然有close調用
- 異常表中增加了內容
Exception table:
from to target type
21 30 41 any
4 52 52 any
0 74 74 Class java/io/IOException
0 78 85 any
好奇怪的異常表,我們查看指令,可以明顯找出最后一條是對應finally處理。前面的呢?經過分析我們發現前面是finally形式的關閉資源。看到這里,結果就很明顯了,自動資源管理實際是上編譯幫助我們做了顯示關閉的邏輯,在JVM執行層面沒有增加新的功能。簡而言之,自動資源管理是編譯器層面的改進,通過擴展語法和增強編譯能力(增加自動的資源關閉能力)來實現自動化編碼。
總結
關于Java的異常的原理,以及自動資源管理的用法和實現方式,就給大家介紹到這里。
-
芯片
+關注
關注
456文章
51178瀏覽量
427265 -
JAVA
+關注
關注
19文章
2974瀏覽量
105147 -
代碼
+關注
關注
30文章
4826瀏覽量
69052 -
編譯
+關注
關注
0文章
661瀏覽量
33041 -
JVM
+關注
關注
0文章
158瀏覽量
12260
發布評論請先 登錄
相關推薦
評論