JVM源碼分析之JVM啟動流程


 

原創申明:本文由公眾號【猿燈塔】原創,轉載請說明出處標注

365篇原創計划”第十四篇。

今天呢!燈塔君跟大家講:

JVM源碼分析之JVM啟動流程


前言:

執行Java類的main方法,程序就能運行起來,main方法的背后,虛擬機究竟發生了什么?如果你對這個感興趣,相信本文會給你一個答案,本文分析的openjdk版本為openjdk-7-fcs-src-b147-27

class BootStrap {
    public static void main(String[] args) {
        for (String str : args) {
            System.out.println(str);
        }
    }
}

java BootStrap -Xms6G -Xmx8G -Xmn3G -Xss512k 
-XX:+UseConcMarkSweepGC -XX:+UseParNewGC

虛擬機的啟動入口位於share/tools/launcher/java.c的main方法,整個流程分為如下幾個步驟:
1、配置JVM裝載環境

2、解析虛擬機參數

3、設置線程棧大小

4、執行Java main方法

 

1、配置JVM裝載環境

Java代碼執行時需要一個JVM環境,JVM環境的創建包括兩部分:JVM.dll文件的查找和裝載。

 

JVM.dll文件的查找

通過CreateExecutionEnvironment方法實現,根據當前JRE環境的路徑和系統版本尋找jvm.cfg文件,windows實現如下:

大概實現邏輯:

1、GetJREPath查找當前JRE環境的所在路徑;

2、ReadKnownVms讀取JRE路徑\lib\ARCH(CPU構架)\JVM.cfg文件,其中ARCH(CPU構架)通過GetArch方法獲取,在window下有三種情況:amd64、ia64和i386;3、CheckJvmType確定當前JVM類型,先判斷是否通過-J-XXaltjvm=-J-XXaltjvm=參數指定,如果沒有,則讀取JVM.cfg文件中配置的第一個類型;4、GetJVMPath根據上一步確定的JVM類型,找到對應的JVM.dll文件;

 

JVM.dll文件的裝載

初始化虛擬機中的函數調用,即通過JVM中的方法調用JVM.dll文件中定義的函數,實現如下:

1、LoadLibrary方法裝載JVM.dll動態連接庫;2、把JVM.dll文件中定義的函數JNI_CreateJavaVMJNI_GetDefaultJavaVMInitArgs綁定到InvocationFunctions變量的CreateJavaVMGetDefaultJavaVMInitArgs函數指針變量上;

 

2、虛擬機參數解析

裝載完JVM環境之后,需要對啟動參數進行解析,其實在裝載JVM環境的過程中已經解析了部分參數,該過程通過ParseArguments方法實現,並調用AddOption方法將解析完成的參數保存到JavaVMOption中,JavaVMOption結構實現如下:

 

AddOption方法實現如下:

這里對-Xss參數進行特殊處理,並設置threadStackSize,因為參數格式比較特殊,其它是key/value鍵值對,它是-Xss512的格式。后續Arguments類會對JavaVMOption數據進行再次處理,並驗證參數的合理性。

 

參數處理

Arguments::parse_each_vm_init_arg方法負責處理經過解析過的JavaVMOption數據,部分實現如下:

這里只列出三個常用的參數:

1、-Xmn:設置新生代的大小NewSize和MaxNewSize;
2、-Xms:設置堆的初始值InitialHeapSize,也是堆的最小值;
3、-Xmx:設置堆的最大值MaxHeapSize;

 

參數驗證

Arguments::check_gc_consistency方法負責驗證虛擬機啟動參數中配置GC的合理性,實現如下:

1、如果參數為-XX:+UseSerialGC -XX:+UseParallelGC,由於UseSerialGC和UseParallelGC不能兼容,JVM啟動時會拋出錯誤信息;2、如果參數為-XX:+UseConcMarkSweepGC -XX:+UseParNewGC,其中UseConcMarkSweepGC和UseParNewGC可以兼容,JVM可以正常啟動;

 

 

3、設置線程棧大小

如果啟動參數未設置-Xss,即threadStackSize為0,則調用InvocationFunctions的GetDefaultJavaVMInitArgs方法獲取JavaVM的初始化參數,即調用JVM.dll函數JNI_GetDefaultJavaVMInitArgs,定義在share\vm\prims\jni.cpp,實現如下:

ThreadStackSize定義在globals.hpp中,根據當前系統類型,加載對應的配置文件,所以在不同的系統中,ThreadStackSize的默認值也不同。

 

4、執行Java main方法

線程棧大小確定后,通過ContinueInNewThread方法創建新線程,並執行JavaMain函數,JavaMain函數的大概流程如下:

 

1、新建JVM實例

InitializeJVM方法調用InvocationFunctions的CreateJavaVM方法,即調用JVM.dll函數

JNI_CreateJavaVM,新建一個JVM實例,該過程比較復雜,會在后續文章進行分析;

 

2、加載主類的class

Java運行方式有兩種:jar方式和class方式。

jar方式

1、調用GetMainClassName方法找到META-INF/MANIFEST.MF文件指定的Main-Class的主類名;

2、調用LoadClass方法加載主類的class文件;

 

class方式

1、調用NewPlatformString方法創建類名的String對象;

2、調用LoadClass方法加載主類的class文件;

 

3、查找main方法

通過GetStaticMethodID方法查找指定方法名的靜態方法,實現如下:

最終調用JVM.dll函數jni_GetStaticMethodID實現

其中get_method_id方法根據類文件對應的instanceKlass對象查找指定方法。

 

4、執行main方法

1、重新創建參數數組;2、其中mainID是main方法的入口地址,CallStaticVoidMethod方法最終調用JVM.dll中的jni_CallStaticVoidMethodV函數,實現如下

jni_invoke_static實現如下:

最終通過JavaCalls::call執行main方法。

365天干貨不斷微信搜索「猿燈塔」第一時間閱讀,回復【資料】【面試】【簡歷】有我准備的一線大廠面試資料和簡歷模板


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM