java虛擬機(二)

JVM函數調用機制

JVM要實現直接由C語言直接調用機器指令,通過兩種方式。
第一、C語言內嵌彙編,內嵌彙編只能實現C語言直接調用匯編指令,而不是機器指令,機器指令和彙編指令還是有很大差距,例:(MOV AX,1234H 對應的機器碼爲:B83412)。
第二、函數指針,通過函數指針,C語言可以將一個變量指向一個函數的首地址,C語言被編譯時,C函數直接被編譯成爲機器指令,而這個函數指針將直接指向這段機器指令的首地址。

1、JVM內部通過函數指針進行函數調用。

函數指針

int (*addPointer) (int a,int b),可以將其指向一個入參爲兩個int的函數。

1.1CallStub函數指針

1.1.1_call_stub_entry例程

1、pc()函數,保存當前例程所對應的一段機器碼的起始位置。
2、generate_all_stub(),得到調用者函數和被調用者函數的參數關係,通過相對位置尋址。
3、CallStub:保存調用者堆棧,保存調用者函數的棧基地址,重新定義棧基地址。
4、CallStub:動態分配堆棧。JVM爲了能夠調用java函數,需要在運行期知道一個java函數的入參大小,然後動態的計算出所需要的堆棧空間。JVM通過堆棧的“寄生”機制,擴展別人的堆棧,存儲自己所需要的數據。call_helper()並沒有直接將java函數的入參傳遞給CallStub(),因爲這個調用者並不直接是java函數自己,而是C函數調用java函數,因此在jvm的編譯階段,並沒有將java函數壓棧,jvm內部也抽象了面向對象的思想,所以傳遞給call_helper()函數的入參不是函數的實際入參,而是實際入參的引用,即指針。指針大小一樣,所以通過計算指針大小與parameter_size就可以得到實際的入參所佔用的堆棧空間=java函數入參數量4+44,每個指針4個字節,44則是保存調用者所執行到的java程序所對應的機器指令的基址和變址。
5、CallStub:調用者保存,這裏的調用者保存與保存調用者堆棧要能夠區分開來,保存調用者堆棧是保存棧基地址,而調用者保存是保存的方法內部的私有數據地址,比如長度爲10的數組,找到第五個元素的時候調用另外一個函數,那麼這個數組當前的位置就得保存到寄存器中。調用者保存主要是針對edi、esi與ebx寄存器而言。
6、CallStub:參數壓棧。動態分配堆棧的時候說過,CallStub爲被調用者分配的堆棧空間大小=java函數入參數量
4+44,這裏的44指的是rdi、rsi、tbx、mxcsr這四個寄存器的位置。而java函數的入參主要通過入參數量與存儲入參寄存器的首地址進行尋找,由於每個入參都是指針,所以知道入參數量便能拿到所有的入參。java函數入參通過循環寫入到CallStub函數中。
7、調用entry_point例程,這個將在內存模型瞭解清楚後進行詳解。例程就是一段預先寫好的函數,jvm通過例程函數在啓動過程中生成機器指令,當執行java函數調用時,jvm直接跳轉到例程所生成的這段機器指令去執行。
8、CallStub:獲得返回值.entry_point例程之後,會有返回值,CallStub會獲取返回值進行處理。

本章總結

物理機器執行函數調用——>用函數指針實現C/C++程序中直接執行本地機器指令——>call_stub例程的學習。

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