天天看點

第1篇-關于Java虛拟機HotSpot,開篇說的簡單點

開講java運作時,這一篇講一些簡單的内容。我們寫的主類中的main()方法是如何被java虛拟機調用到的?在java類中的一些方法會被由c/c++編寫的hotspot虛拟機的c/c++函數調用,不過由于java方法與c/c++函數的調用約定不同,是以并不能直接調用,需要javacalls::call()這個函數輔助調用。(我把由c/c++編寫的叫函數,把java編寫的叫方法,後續也會延用這樣的叫法)如下圖所示。

第1篇-關于Java虛拟機HotSpot,開篇說的簡單點

從c/c++函數中調用的一些java方法主要有:

(1)java主類中的main()方法;

(2)java主類裝載時,調用javacalls::call()函數執行checkandloadmain()方法;

(3)類的初始化過程中,調用javacalls::call()函數執行的java類初始化方法<clinit>,可以檢視javacalls::call_default_constructor()函數,有對<clinit>方法的調用邏輯;

(4)我們先省略main方法的執行流程(其實main方法的執行也是先啟動一個javamain線程,套路都是一樣的),單看某個javathread的啟動過程。javathread的啟動最終都要通過一個native方法java.lang.thread#start0()方法完成的,這個方法經過解釋器的native_entry入口,調用到了jvm_startthread()函數。其中的static void thread_entry(javathread* thread, traps)函數中會調用javacalls::call_virtual()函數。javathread最終會通過javacalls::call_virtual()函數來調用位元組碼中的run()方法;

(5)在systemdictionary::load_instance_class()這個能展現雙親委派的函數中,如果類加載器對象不為空,則會調用這個類加載器的loadclass()函數(通過call_virtual()函數來調用)來加載類。

當然還會有其它方法,這裡就不一一列舉了。通過javacalls::call()、javacalls::call_helper()等函數調用java方法,這些函數定義在javacalls類中,這個類的定義如下:

如上的函數都是自解釋的,通過名稱我們就能看出這些函數的作用。其中javacalls::call()函數是更低一層的通用接口。java虛拟機規範定義的位元組碼指令共有5個,分别為invokestatic、invokedynamic、invokestatic、invokespecial、invokevirtual幾種方法調用指令。這些call_static()、call_virtual()函數内部調用了call()函數。這一節我們先不介紹各個方法的具體實作。下一篇将詳細介紹。  

我們選一個重要的main()方法來檢視具體的調用邏輯。如下基本照搬r大的内容,不過我略做了一些修改,如下:

假設我們的java主類的類名為javamainclass,下面為了區分java launcher裡c/c++的main()與java層程式裡的main(),把後者寫作javamainclass.main()方法。

從剛進入c/c++的main()函數開始:

啟動并調用hotspot虛拟機的main()函數的線程執行的主要邏輯如下:

在如上線程中會啟動另外一個線程執行javamain()函數,如下:

以上步驟都還在java launcher的控制下;當控制權轉移到javamainclass.main()方法之後就沒java launcher什麼事了,等javamainclass.main()方法傳回之後java launcher才接手過來清理和關閉jvm。

下面看一下調用java主類main()方法時會經過的主要方法及執行的主要邏輯,如下:

後面3個步驟是在編譯執行的模式下,不過後續我們從解釋執行開始研究,是以需要為虛拟機配置-xint選項,有了這個選項後,java主類的main()方法就會解釋執行了。

在調用java主類main()方法的過程中,我們看到了虛拟機是通過javacalls::call()函數來間接調用main()方法的,下一篇我們研究一下具體的調用邏輯。