談談JVM this關鍵字和異常表的作用
你知道的越多,不知道的就越多,業(yè)余的像一棵小草!
你來,我們一起精進!你不來,我和你的競爭對手一起精進!
編輯:業(yè)余草
juejin.cn/post/7003717101537984525
推薦:https://www.xttblog.com/?p=5357
先從段代碼說起
我歷史文章中已經(jīng)寫過關于 this 這個關鍵字的秘密了(《阿里面試題:Java中this和super關鍵字的底層實現(xiàn)原理》),不知道還有多少人記得。今天我們繼續(xù)聊聊 this 的底層實現(xiàn)以及 Java 中 Exception 的底層實現(xiàn)。
關于異常和this的
代碼
public class ExceptionTest {
public void test(){
try{
InputStream inputStream = new FileInputStream("test.cpp");
ServerSocket serverSocket = new ServerSocket(3306);
serverSocket.accept();
}catch (FileNotFoundException notFound){
}catch (IOException ioException){
}catch (Exception exception){
}finally{
System.out.println("finally");
}
}
}
反編譯截圖如下:

?「在Java代碼層面沒有參數(shù),但是在字節(jié)碼層面是有參數(shù)的。」
?
隱藏的實例方法參數(shù)就是 「this」
對于Java類中的每一個實例方法(「非static方法」),其在編譯后所生成的字節(jié)碼當中,方法參數(shù)的數(shù)量總是會比源代碼中方法參數(shù)的數(shù)量多一個(this),它位于方法的第一個參數(shù)位置處;這樣,我們就可以在Java的實例方法中「使用this去訪問當前對象的屬性以及其他方法。」 這個操作是在編譯期間完成的,即由javac編譯器在編譯的時候將對this的訪問轉化為對一個普通實例方法參數(shù)的訪問,接下來在運行期間,由JVM在調用實例方法時,自動向實例方法傳入該this參數(shù)。所以,在實例方法的局部變量表中,「至少會有一個指向當前對象的局部變量。」
再解釋max_locals(最大局部變量)
該代碼 max_locals 為什么是 4?

代碼的try中有兩個顯式定義的局部變量,這就有2個了。 再加上隱藏的this,這一共就有3個了。 最后,留一個給Exception局部變量,雖然代碼中有多個catch,但是,如果有異常的話只有一個catch會被執(zhí)行。可以理解為這個Exception局部變量會在運行期間等待著被傳入catch(類似作為它的參數(shù)傳入)。這樣一共就有4個局部變量了。
異常表
在說異常表之前,先復習一下字節(jié)碼Code屬性的結構
「
attribute length」 表示attribute所包含的字節(jié)數(shù),「不包含」attribute_name_index和attribute_length字段。「
max_stack」 表示這個方法運行的任何時刻所能達到的操作數(shù)棧的最大深度。「
max_locals」 表示方法執(zhí)行期間創(chuàng)建的局部變量的數(shù)目,包含用來表示傳入的參數(shù)的局部變量。「
code_length」 表示該方法所包含的字節(jié)碼的字節(jié)數(shù)以及具體的指令碼。具體字節(jié)碼即是該方法被調用時,虛擬機所執(zhí)行的字節(jié)碼。
「
exception_table」 這里存放的是處理異常的信息。每個 「 exception_table」 表項由 「start_pc」 ,**end_pc「,」handler_pc「,」catch_type** 組成。「
start_pc」 和 「end_pc」 表示在 「code」 數(shù)組中的從 「start_pc」 到 「end_pc」 處(「包含start_pc,不包含end pc「)的」指令」拋出的異常會由這個表項來處理。「
handler_pc」 表示「處理異常的代碼的開始處」。**catch_type** 表示會被處理的異常類型,它「指向」常量池里的一個異常類。當 「catch_type為0時,表示處理所有的異常」
閱讀字段表

?「上面的異常表明明有一個Exception最頂層的異常,為啥還多出一個any來表示捕獲所有異常」?
?
「只能說代碼層面Exception是最頂層的,但是在字節(jié)碼層面就不一樣了。any表示哪些連Exception都無法處理的異常。」
再者,如果在方法簽名上throws異常
代碼
public void test() throws NullPointerException,FileNotFoundException,IOException{
try{
InputStream inputStream = new FileInputStream("test.cpp");
ServerSocket serverSocket = new ServerSocket(3306);
serverSocket.accept();
}catch (FileNotFoundException notFound){
}catch (IOException ioException){
}catch (Exception exception){
}finally{
System.out.println("finally");
}
}
多了點東西

總之,Java字節(jié)碼對于異常的處理方式:
統(tǒng)一采用異常表的方式來對異常進行處理。 在jdk 1.4.2之前的版本中,并不是使用異常表的方式來對異常進行處理的,而是采用特定的指令方式。 當異常處理存在finally語句塊時,現(xiàn)代化的JVM采取的處理方式是將finally語句塊的字節(jié)碼拼接到每一個catch塊后面。 換句話說,程序中存在多少個catch塊,就會在每一個catch塊后面重復多少個finally語句塊的字節(jié)碼。
