java虛擬機面試乾貨【柒】_class的執行


上篇文章:java虛擬機面試乾貨【陸】_垃圾回收:垃圾收集器


在之前的文章中,我們大致總結了JVM的執行過程如下:




當class文件被類加載器加載到內存(棧)後,由執行引擎對字節碼進行解析或等效處理後,最後輸出結果。下面就說說這個執行引擎是如何工作的。


棧幀


棧幀(Stack Frame)是用於支持虛擬機進行方法調用方法執行的數據結構,它是虛擬機運行時數據區中的虛擬機棧(Virtual Machine Stack)的棧元素。

棧幀存儲了方法的局部變量表操作數棧動態連接方法返回地址和一些額外的附件信息等信息。編譯代碼的時候,棧幀需要的局部變量表大小,操作數棧的深度等都已經存儲在Code屬性中。棧幀大小不受運行期變量數據的影響。每一個方法從調用開始至執行完成過程,都是對應着一個棧幀在虛擬機棧裏面從入棧到出棧的過程。

對於執行引擎來說,在活動線程中,只有位於棧頂的棧幀纔是有效的,稱爲當前棧幀(Current Stack Frame)。與這個棧幀相關聯的方法稱爲當前方法(Current Method)。執行引擎的所有字節碼指令都是隻針對當前棧幀進行操作的,典型的棧幀結構如下:


這裏寫圖片描述


下面說說棧幀幾個重要組成部分:


局部變量表

存放方法參數和局部變量。以變量槽存放數據,變量槽有索引,從0開始。0是當前對象的引用,用this關鍵字可以獲取。其餘是其他參數和局部變量,虛擬機通過索引取值。局部變量不像類變量,不會賦初始值,沒賦值過就不能使用。


操作數棧

方法進入時是空的,許多指令會不停的吧數據出入棧。算數指令會對棧內數據計算,寫回結果;方法調用會用操作數棧傳遞參數。


動態鏈接

指向運行時常量池中,該棧楨所屬方法的引用。爲了支持方法調用過程中的動態鏈接。


方法返回地址

方法退出時,需要回到上層調用的位置。棧楨需要保存這個地址。



方法調用


方法調用並不等同於方法的執行,方法調用的唯一任務是:確定被調用方法的版本(即調用哪個方法)。由於Java編譯成class文件時,只是保存了方法的符號引用,而沒有直接引用(即方法的內存地址),故這裏涉及到解析和分派兩個步驟。


解析


所有方法調用的目標方法都是在class文件中一個常量池的符號引用,故在類加載的解析階段會把一部分符號引用轉化爲直接引用。此解析成立的前提是:方法在程序真正運行之前就有一個可以確定的調用版本,且此方法的調用版本在運行期是不可改變的。符合此前提的方法的調用稱爲解析(Resolution),也就是說解析式一個靜態過程。

在Java中,符合解析的方法主要包括靜態方法和私有方法兩類,前者是於類直接關聯的;後者在外部不可訪問。由於其特點決定其無法被重寫或覆蓋,故適合在類加載時加載解析。其實final修飾的方法,也是符合此約定的,會在類加載時解析。但由於其是使用invokevirtual調用的,故這裏單獨給出。

Java提供了五種調用方法的字節碼指令:


invokestatic(調用靜態方法);

invokespecial(調用實例構造器,私有方法和父類方法);

invokevirtual(調用所有的虛方法);

invokeinterface(調用接口方法,會再運行時確定一個接口的實現);

invokedynamic(先在運行時動態的解析出調用點限定符所引用的方法再執行該方法);

其中invokestatic和invokespecial指令調用的方法,都可以在解析階段唯一確定調用版本,也就是會再類加載時會被解析。


分派


分派是實現Java多態性的基礎,可能是靜態的也可能是動態的,根據分派依據的宗量數又可分爲單分派和多分派。


靜態分派

依賴靜態類型來定位方法執行版本的分派動作,稱爲靜態分派。靜態分派的最典型的應用就是方法重載。靜態分派發生在編譯階段,因此確定靜態分派的動作實際上不是由虛擬機來執行的。


方法重載參數的匹配規則:char->int->long->float->double->裝箱類->裝箱類的接口類型->裝箱類從下往上的父類型->可變參數類型。也就是說首先會進行自動轉型,然後是裝箱操作,接着是裝箱後的接口(實現了多個接口則優先級一致),再然後父類(從下往上遞歸),最後纔是可變參數。


動態分派

在運行期間根據實際類型來確定方法執行版本的分派調用過程稱爲動態分派。這和方法重寫有着很密切的關聯。涉及到invokevirtual指令,這個指令的多態查找規則如下:

1.找到操作數棧頂的第一個元素所指對象的實際類型 C ;

2.若類型C中找到與常量中的描述符和簡單名稱都匹配的方法,則進行訪問權限校驗,通過則返回此方法的直接引用,查找結束;未通過則返回java.lang.IllegalAccessError;

3.若沒找到匹配的方法則按照繼承關係從下往上依次對C的父類進行第2部的搜索和驗證過程;

4.若最終沒有找到合適的方法,則拋出java.lang.AbstractMethodError;


單分派和多分派

宗量對方法的接收者與方法的參數的統稱。根據一個宗量對目標方法進行選擇爲單分派;根據一個以上宗量對目標方法進行選擇爲多分派。




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