JVM系列(三)——方法的調用


         Java代碼在執行的時候,分爲解釋執行(通過解釋器)和編譯執行(通過編譯器)

         一、棧幀

棧幀是用於支持虛擬機進行方法調用和方法執行的數據結構,在每次進入一個方法的時候,都會生成該方法的棧幀,併入棧。當方法執行完時,則將對應的棧幀出棧。每個方法對應一個棧幀,每次只有棧頂的棧幀有效,這樣,各個方法的調用、內部變量的使用便不會相互干擾。

         在一個棧幀中,包含了局部變量表、操作數棧、動態鏈接、方法返回地址等各個部分的作用和數據結構。

1、 局部變量表。

局部變量表,顧名思義,用於存儲某個方法內部的變量和方法參數。變量表中,以變量槽(Slot)作爲基本單位,來存儲各個變量。能夠存儲基本類型和reference、returnAddress類型。

2、 操作數棧

局部變量表用於存儲數據,而對於數據的操作(算數運算),則需要使用操作數棧,通過入棧出棧進行。例如進行加操作時,將已經存入棧的最頂層的2個操作數取出,進行求和,並將結果存入棧。

3、 動態鏈接

主要是爲了支持運行時才進行的關聯的引用,例如支持多態性,而指向運行時常量池的引用。

4、 方法返回地址

用於在方法執行完畢或者遇到異常需要返回時,給出外部函數的PC地址。並存儲一些恢復外部函數繼續執行時的必要信息。

二、方法調用

方法調用與方法執行不同,方法調用的唯一任務是確定調用方法的版本。因爲在class文件中存儲的只是符號引用,而不是方法在實際運行時候的入口地址,故需要在類加載期間甚至到運行期間才能夠確定目標方法的直接引用。

要確定方法的實際入口地址,有兩種情況:

1、 編譯期間能夠確定。主要有靜態方法和私有方法兩大類。前者與類型直接關聯,後者不可被訪問,二者都無法重寫出其他版本,故在類加載的時候便可進行解析。

2、 編譯期間不能夠確定,只能在運行期間確定。由於方法可能會被 “重寫”,故無法在編譯期間確定。

在多態的情況下,例如

Human man = new Man();

Human women = new Women();

局部變量表中的Reference類型的man,指向堆中生成的Man對象,而womon則指堆中的Women對象,在執行的時候,則會根據實際的指向進行調用了。這種,在運行階段確定的分派動作成爲動態分派。

但“重載”的函數是在編譯期間確定的,他屬於靜態分派。在編譯階段,編譯期會確定一個“最合適”的方法(需要類型轉換次數最少…等一些規則),來進行入口函數地址的確定。“重載”方法並沒有像“重寫”的方法一樣,有這樣一個引用類型以供確定,他的方法的不同是在同一個類(重載)而非不同類中(重寫),故無法實現動態分配。

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