異常表
每一個try語句塊catch的異常都與異常表中的一項相對應,異常表中的每一項都包括:
起點
終點,始終把catch異常位置的pc指針偏移量的最大值大1
處理異常時跳轉到的字節碼序列中的pc指針偏移量
被catch的異常類的常量池索引
例如:
public class Test {
public static void main(String[] args) {
try {
Class.forName(“java.lang.String”);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
用javap –c查看字節碼如下:
Compiled from “Test.java”
public class Test extends java.lang.Object{
public Test();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object.”<init>”:()V
4: return
public static void main(java.lang.String[]);
Code:
0: ldc #2; //String java.lang.String
2: invokestatic #3; //Method java/lang/Class.forName:(Ljava/lang/String;)Ljava/lang/Class;
5: pop
6: goto 14
9: astore_1
10: aload_1
11: invokevirtual #5; //Method java/lang/ClassNotFoundException.printStackTrace:()V
14: return
Exception table:
from to target type
0 6 9 Class java/lang/ClassNotFoundException
}
可見ClassNotFoundException異常可能會在0~6之間拋出,9開始處的代碼處理此異常。
當產生異常的時候,jvm將會在整個異常表中搜索與之匹配的項,如果當前pc在異常表入口所指的范圍內,並且所拋出的異常是此入口所指向的類或者其子類,則跳轉到對應的處理代碼繼續執行。
方法可能會拋出哪些已檢查異常
Class文件的attribute_info中保存有Exceptions屬性,記錄著每個方法throws的異常信息。具體的可以查看class類文件格式相關的文章。
athrow指令從棧頂彈出Throwable對象引用,拋出異常。
finally語句
在jvm規范中,finally語句是通過jsr/jsr_w與ret指令實現的。當執行jsr/jsr_w的時候將finally執行完成後的返回地址壓入棧中,進入finally後會馬上將此地址保存到一個局部變量中,執行完成後,ret從此局部變量中取出返回地址。???為什麼會先把返回地址保存到局部變量中呢???因為,當從finally語句返回的時候需要將返回地址成棧中彈出,當finally語句非正常結束(break,continue,return, 拋異常)的時候就不用再考慮這個問題。
以下是jvm規范中Compiling finally的一段:
void tryFinally() {
try {
tryItOut();
} finally {
wrapItUp();
}
}
the compiled code is
Method void tryFinally()
0 aload_0 // Beginning of try block
1 invokevirtual #6 // Method Example.tryItOut()V
4 jsr 14 // Call finally block
7 return // End of try block
8 astore_1 // Beginning of handler for any throw
9 jsr 14 // Call finally block
12 aload_1 // Push thrown value
13 athrow // and rethrow the value to the invoker
14 astore_2 // Beginning of finally block
15 aload_0 // Push this
16 invokevirtual #5 // Method Example.wrapItUp()V
19 ret 2 // Return from finally block
Exception table:
From To Target Type
0 4 8 any
當tryItOut排除任何異常後都將會被異常表中的any項捕獲,執行完finally後,會執行athrow指令將異常拋出。
從jdk的某一個版本開始就不會編譯出編譯出含jsr/jsr_w、ret的字節碼瞭,因為有指令上的缺陷,導致jvm的檢驗和分析系統出現漏洞。
再說finally的非正常退出
在finally中使用break、continue、return、拋出異常等認為是finally的非正常結束。非正常結束的時候,ret指令不會被執行,很可能會出現意想不到的結果。如:
public class Test {
public static boolean test(boolean b) {
while (b) {
try {
return true;
} finally {
/**//*
break; 始終返回false
continue; javac編譯再java執行會出現死循環
在eclipse中甚至會出現報錯:提示找到不main class
return false; 始終返回false
throw new RuntimeException(“”); 拋出異常
*/
}
}
return false;
}
public static void main(String[] args) {
&nb