JVM方法呼叫指令

2021-09-01 06:52:04 字數 1161 閱讀 6998

終於把inside jvm這本看完了,好久沒這麼細緻的看一本書了。

好多人都寫了文章討論jvm如何實現多型的,我只是簡單做個筆記。

類的位元組碼結構有個常量池,其中就存放了這個類中呼叫的方法的符號引用,這些符號引用實際上是放在一些特殊型別(constant_nameandtype_info)的常量池入口中,呼叫方法有四個指令,invoke_static,invoke_special,invoke_virtual,invoke_inte***ce。jvm指令是呼叫某個棧上的變數的方法,因此這個變數就有可能指向的是物件或者介面。

剛才提到直接引用,這個在常量池解析時,會將符號引用替換為直接引用。而對於不同情況,直接引用代表的東東是不同的。對於靜態類變數,方法指的是本地指標,對於例項方法,指的就是方法表偏移量。

對於類繼承關係而言,從object開始,所有類的方法的描述的方法在方法表中的索引(也就是在方法表中的位置的偏移量)都是一樣的,比如tostring方法,無論是在object,還是在它的子類裡,子類的子類裡,這個偏移量都是一樣的,因此如果棧上的那個變數指向的是乙個類例項而不是乙個介面,那麼就可以替換這個方法呼叫的常量池入口的符號引用為這個偏移量,真正在執行時呼叫這個方法時,直接找到那個物件對應的類的方法表,用那個偏移量一下子就可以定位到具體的方法了。

方法表是類基本資訊的一部分,這個可以理解成乙個中間表,將偏移量這個邏輯的index與方法真實的本地指標關聯了起來(指向方法區,這個區是跟堆一樣,所有執行緒共享的)。

可以看到,如果是呼叫靜態編譯的方法,那麼呼叫時,直接就可以找到本地指標去執行那個方法,如果是指向類的變數呼叫例項方法,那麼就拿放在常量池中的偏移量到類方法表中去查到那個本地指標,再去執行那個方法。

jvm還有一些指令,加了quick字尾,對於上面兩種情況,在常量池解析後,實際上就會直接將原來的指令換為quick版本,將直接引用直接作為指令的運算元,這樣就不用跑到常量池中去查一次了。

另外,每呼叫乙個方法時,當前執行緒就會建立乙個frame,然後把它放到當前stack中,每個frame都包括區域性變數區,運算元棧和frame變數區。那麼乙個方法裡呼叫了另乙個方法怎麼辦呢?那就再建立乙個frame,去執行那個frame裡所有的指令,執行完了再返回剛才那個frame繼續執行。因此呼叫另乙個方法是比較耗時的,這才有了方法內聯這個優化。在編譯類的時候,每個方法占多大記憶體都是計算好的,執行時中的方法區里儲存了這些元資料,建立frame時就根據方法區的內容去建立就好了。

JVM常用指令

1 指令集的分類 指令集大體上可分為兩類 基於棧的指令集 基於暫存器的指令集。基於棧的指令集是利用出棧和進棧來實現的,基於暫存器的指令集是通過暫存器的特性來實現的,基於暫存器的指令集複雜但是高效。2 基於棧的指令集 大多數指令並不包含運算元,只有乙個操作碼,指令引數都存放於運算元棧中,操作碼的長度為...

jvm是怎樣呼叫方法的

jvm內部有五種呼叫方法的指令 invokeinte ce 用以呼叫介面方法,在執行時搜尋乙個實現了這個介面方法的物件,找出適合的方法進行呼叫。invoke inte ce method invokevirtual 指令用於呼叫物件的例項方法,根據物件的實際型別進行分派 invoke instanc...

方法呼叫指令invoke

1 方法呼叫 分派 執行過程 jvm呼叫方法有五條指令,分別是 1 invokestatic,用來呼叫static方法 類方法 2 invokespecial,用來呼叫需要特殊處理的例項方法,私有方法,父類方法 super.初始化方法。在物件的建立過程中,new之後很多都會執行方法,就是依賴位元組碼...