前言:
本篇以.Net 7.0.2 CLR 和 OpenJDk19參照,解析下它們各自調用函數的異同。
以下爲個人理解。
概述
JDK大約5.9G,CLR大約7.6G,兩者相差1.7G左右。
root@tang-virtual-machine:/home/tang# cd jdk-jdk-19-36
root@tang-virtual-machine:/home/tang/jdk-jdk-19-36# du -sh
5.9G .
root@tang-virtual-machine:/home/tang/Downloads# du -sh
7.6G .
JVM事先把需要運行的函數編譯好存放在某個地址,此後通過呼叫樁(call_stub)進行調用,這裏的某個地址裏面包含了需要運行的函數頭地址,在裏面跳轉運行。
CLR是一邊運行一邊編譯,遇到哪個函數就編譯哪個,編譯完成後跳轉到此函數的函數頭(pCode)地址進行運行。
具體的呢?先看JVM,它的調用如下
Main-》-》Clone3-》JavaMain-》InitializeJVM-》create_vm-》init_globals-》generate_call_stub() //這裏省略了部分
generate_call_stub函數對需要運行的函數進行編譯,它的代碼實際上是生成機器碼,然後返回函數樁頭
StubRoutines::_call_stub_entry = generate_call_stub(StubRoutines::_call_stub_return_address);
_call_stub_entry
也就是call_stub接受的那個地址。_call_stub_entry裏面包含了需要運行函數的函數頭地址,在call_stub裏面進行地址跳轉調用。此處可以的原理可以看下以前文章:點擊此處查看原理
那麼call_stub的調用是什麼呢?
Main-》-》Clone3-》JavaMain-》InitializeJVM-》create_vm-》initialize_java_lang_classes-》call_stub
可以看到generate_call_stub和call_stub兩者在create_vm函數處分爲兩處,首先調用了generate_call_stub對函數進行了編譯,再調用call_stub對編譯好的函數進行了調用。
再看CLR,以調用託管的Main函數入口爲例。常例,先看下它的調用
Main-》RunMain-》MethodDescCallSite::Call-》MethodDescCallSite::CallTargetWorker-》CallDescrWorkerWithHandler-》CallDescrWorkerInternal-》ThePreStub-》PreStubWorker
如果Main函數裏面調用了其它函數,比如如下:
static void Main(string[] args)
{
Console.Write("Tian xia feng yun chu wo bei");
}
Main裏面調用了Console.Write函數,當CLR運行到此函數處,又把上面的調用運行了一邊
MethodDescCallSite::Call-》MethodDescCallSite::CallTargetWorker-》CallDescrWorkerWithHandler-》CallDescrWorkerInternal-》ThePreStub-》PreStubWorker
直到Console.Write函數編譯和運行都完畢,才返回Main函數繼續執行。關於這一點可以參照以前的文章:點擊查看原理
結尾:
作者:江湖評談
關注我,帶你瞭解高價值和好玩的技術