1. JVM體系結構
(1)類加載器(`ClassLoader`)(用來裝載`.class`文件)
(2)執行引擎(執行字節碼,或者執行本地方法)
(3)運行時數據區(方法區、堆、java棧、本地方法棧、PC寄存器)
2. JVM的生命周期
1.JVM實例對應了一個獨立運行的java程序,它是進程級別;
a)啟動。啟動一個java程序時,一個JVM實例就產生了,任何一個擁有`public static void main(String
[] args)`方法的class都可以作為JVM實例運行的起點
b)運行。`main()`作為該程序的初始線程的起點,任何其他線程均由該線程啟動。JVM內部有兩種線程:守護線
程和非守護線程,`main()`屬于非守護線程守護線程通常由JVM自己使用,java程序也可以標明自己創建的線程
是守護線程
c)消亡。當程序中的所有非守護線程都終止時,JVM才退出;若安全管理器允許,程序也可以使用`Runtime`類
或者`System.exit()`來退出
2.JVM執行引擎實例則對應了屬于用戶運行程序的線程,它是線程級別的;
3. JVM啟動
JVM工作原理和特點主要是指操作系統裝入JVM,是通過JDK中java.exe
來完成,通過下面4步來完成JVM環境.
1.創建`JVM`裝載環境和配置
2.裝載`JVM.dll`
3.初始化`JVM.dll`并掛界到`JNIENV`(`JNI`調用接口)實例
4.調用`JNIENV`實例裝載并處理`class`類
3.1 啟動代碼分析
main
函數在openjdk\jdk\src\share\bin\main.c
文件中。簡單流程分析如下:
1.SelectVersion->LocateJRE,定位jre的位置。因為在不同的操作系統上定位jre的方式不同,相關代碼就
和平臺相關了,windows相關的一些代碼在`openjdk\jdk\src\windows`下,而`linux`和`solaris`相關
代碼在`openjdk\jdk\src\solaris`下。這部分調用到的代碼都在`{os}\bin\java_md.c`中。簡單的說,
windows中,LocateJRE要通過查找注冊表來定位jre的位置,而Linux下可能需要讀取環境變量等。
2.CreateExecutionEnvironment->GetJVMPath得到jvm的路徑,其實就是找到相應的動態鏈接
庫,具體到linux上,就是`libjvm.so`。
3.LoadJavaVM,linux上是加載'libjvm.so',windows上是加載`jvm.dll`,導
出`JNI_CreateJavaVM`、`JNI_GetDefaultJavaVMInitArgs`等函數。
4.獲取classpath。
5.ContinueInNewThread(&ifn,argc,argv,jarfile,classname,ret);在一個新線程中啟動jvm。
ContinueInNewThread的功能是:調用`GetDefaultJavaJVMInitArgs`(其實就
調用`JNI_GetDefaultJavaVMInitArgs`)獲取虛擬機初始化參數,設定線程棧的大小,
創建java程序需要的各項參數。然后調用下面函數 `ContinueInNewThread0(JavaMain,
threadStackSize, (void*)&args);` ;因為不同操作系統創建線程的方式不同,所以又進
入os相關的代碼,`{os}\bin\java_md.c`中的`int ContinueInNewThread0(JNICALL
*continuation)(void *), jlong stack_size,void *args)`函數。
閱讀其源碼,可知linux上是通過`pthread_create`創建線程,Solaris通過`thr_create`創建線程,
而`windows`則通過`_beginthreadex`創建線程。
在新線程中調用JavaMain函數,即openjdk\jdk\src\share\bin\main.c中的 `int JNICALL
JavaMain(void *_args)`,它負責加載要運行的的java class,并調用class的main方法。
處理步驟是:
1).InitalizeJVM->CreateJavaVM(即通過JNI調用JNI_CreateJavaVM),創建jvm。
2).LoadMainClass加載main class,并確保main方法的簽名是正確的。
3).mainID = (*env)->GetStaticMethodID(env, mainClass, "main","([Ljava/lang/String;)V");通過JNI得到main方法的method信息。
4).mainArgs = NewPlatformStringArray(env, argv, argc);為main方法準備參數數組。
5).(*env)->CallStaticVoidMethod(env, mainClass, mainID, mainArgs);通過JNI調用main方法。
6).ret = (*env)->ExceptionOccurred(env) == NULL ? 0 : 1;處理異常
7).DetachCurrentThread
8).DestroyJavaVM 銷毀JVM
下面以windows的實現進行分析:
首先查找jre路徑,Java通過
GetApplicationHome api
來獲得當前的java.exe
絕對路徑(例如我電腦上的e:\program \java\jdk\bin\java.exe
),那么它會截取到絕對路徑e:\program\java\jdk
,判斷e:\program\java\jdk\jvm.dll
文件是否存在,如果存在就把e:\program\java\jdk
,作為jre路徑;如果不存在則判斷e:\program\java\jdk\jre\jvm.dll
是否存在,如果存在則將e:\program\java\jdk\jre
作為jre路徑。如果不存在調用GetPublicJREHome
查HKEY_LOCAL _MACHINE\Software\JavaSoft\Java Runtime Environment\"當前JRE版本號"\JavaHome
的路徑作為jre路徑。