Java虛擬機啟動過程探索
一、序言
當我們在編寫Java應用的時候,很少會註意Java程序是如何被運行的,如何被操作系統管理和調度的。帶著好奇心,探索一下Java虛擬機啟動過程。
1、素材準備
從Java源代碼
、Java字節碼
、Java虛擬機
、操作系統
四個角度分解啟動過程。
public class HelloWorld { public static void main(String[] args) { System.out.println("HelloWorld!"); } }
2、源代碼生成字節碼
利用Java環境提供的可執行命令javac
將源代碼編譯成字節碼文件,編譯後的字節碼文件與平臺無關,可跨平臺運行。註意區分javac
命令是一個獨立的編譯應用,源代碼編譯完成,進程終止。java
命令啟動的虛擬機進程的編譯過程是將字節碼指令編譯成匯編指令(二進制指令)。
3、虛擬機解析字節碼
Java字節碼無法直接在操作系統上創建進程,因此需要借助已經啟動的虛擬機進程來解析字節碼,處理字節碼有兩種常見方式:解釋型
和編譯型
。
在命令行中每運行java
命令代表啟動一個Java虛擬機進程,各虛擬機相互獨立,通過命令行參數分別對虛擬機進程進行配置。
Java虛擬機準備啟動完畢後,便可以依次解析字節碼指令,正式運行Java代碼
部分。
4、操作系統管理虛擬機
操作系統通過進程管理和調度Java虛擬機,無法感知虛擬機間接解析Java字節碼部分。Java字節碼通過虛擬機的抽象,完成瞭在操作系統上運行。
二、Java虛擬機
當運行Java應用時,需要先安裝Java環境,然而安裝的Java環境與Java應用有什麼關系,Java應用是如何運行起來的,下面一探究竟。
二進制可執行程序${JAVA_HOME}/bin/java
是C++編寫經過GCC編譯器編譯後形成的,探索Java虛擬機的運行原理,首先需要找到相應的源碼。
當在安裝Java環境時,會看到一個src.zip
壓縮文件,解壓後裡面launcher/java.c
文件便是可執行文件java
命令的主要源碼。
虛擬機的啟動入口位於launcher/java.c
的main方法
,整個流程分為如下幾個步驟: 配置JVM裝載環境;解析虛擬機參數;設置線程棧大小;執行Java main方法
(一)配置JVM裝載環境
從操作系統加載環境變量、硬件信息等運行環境信息,為後續創建JVM進程做準備。
(二)命令行參數解析
裝載完JVM環境之後,需要對啟動時命令行參數進行解析,該過程通過ParseArguments方法
實現,並調用AddOption方法
將解析完成的參數保存到JavaVMOption中。
比如常見的JavaVMOption參數在此步驟解析:
-Xms:設置堆的初始值InitialHeapSize,也是堆的最小值;
-Xmx:設置堆的最大值MaxHeapSize;
JVM調優各參數解析便是在此步驟完成的。
(三)執行main方法
線程棧大小確定後,通過ContinueInNewThread方法
創建新線程,並執行JavaMain函數,大概流程如下:
1、新建JVM實例
InitializeJVM方法調用InvocationFunctions的CreateJavaVM方法,即調用JVM.dll函數JNI_CreateJavaVM,新建一個JVM實例,該過程比較復雜。
2、加載入口類
通常在命令行中運行如下命令即指明入口類路徑
# 直接指名入口類路徑 java HelloWorld.class # 通過包類配置入口類路徑 java -jar HelloWorld.jar
3、查找main方法
通過GetStaticMethodID方法查找指定main方法名的靜態方法。
4、執行main方法
通過JavaCalls::call
回調執行main方法。需要註意的是,這裡執行main方法不是Java語言的方法,是經過虛擬機解釋(或者編譯)後,操作系統能夠理解的二進制可執行方法。
三、解析字節碼
(一)解釋字節碼
1、基於棧指令集
iconst_1 將 1 放入棧頂 iconst_1 將 1 放入棧頂 iadd 將棧頂的 2 個數相加後結果放入棧頂 istore_0 將相加的結果放入局部變量表
基於棧的指令集優點是虛擬機解釋器是可跨平臺移植的,換句話說不同平臺的虛擬機解釋器代碼可以復用。
2、基於寄存器指令集
mov eax,1 把 EAX 寄存器的值設為 1 add eax,1 再把這個值加 1 ,結果保存在瞭 EAX 寄存器
基於寄存器指令集的優點是執行速度相對於棧較快,原因是出棧入棧本身就涉及瞭大量的指令,而且棧是在內存中實現的,更底層的匯編指令性能更高。
基於寄存器指令集的缺點是虛擬機解釋器是不可跨平臺移植,需要針對不同平臺的虛擬機做不同實現。考慮到不同平臺已經使用不同的虛擬機程序,因此此過程多用戶透明。
虛擬機通過解釋器來翻譯字節碼文件中的指令比較順其自然,可是對於服務器端高頻執行的程序來說,中間的翻譯過程相對耗時。解釋字節碼的方式適用於對啟動性能要求高,並且執行頻率較低的應用程序。
(二)編譯字節碼
最初,JVM 中的字節碼是由解釋器( Interpreter )完成編譯的,當虛擬機發現某個方法或代碼塊的運行特別頻繁的時候,就會把這些代碼認定為熱點代碼
。
為瞭提高熱點代碼的執行效率,在運行時,即時編譯器(JIT,Just In Time)會把這些代碼編譯成與本地平臺相關的機器碼,並進行各層次的優化,然後保存到內存中。
在 HotSpot 虛擬機中,內置瞭兩種 JIT,分別為C1 編譯器
和C2 編譯器
,這兩個編譯器的編譯過程是不一樣的。
1、C1 編譯器
C1 編譯器是一個簡單快速的編譯器,主要的關註點在於局部性的優化,適用於執行時間較短或對啟動性能有要求的程序,也稱為Client Compiler
,例如,GUI 應用對界面啟動速度就有一定要求。
2、C2 編譯器
C2 編譯器是為長期運行的服務器端應用程序做性能調優的編譯器,適用於執行時間較長或對峰值性能有要求的程序,也稱為Server Compiler
,例如,服務器上長期運行的 Java 應用對穩定運行就有一定的要求。
3、分層編譯
分層編譯將 JVM 的執行狀態分為瞭 5 個層次:
第 0 層:程序解釋執行,默認開啟性能監控功能(Profiling),如果不開啟,可觸發第二層編譯;
第 1 層:可稱為 C1 編譯,將字節碼編譯為本地代碼,進行簡單、可靠的優化,不開啟 Profiling;
第 2 層:也稱為 C1 編譯,開啟 Profiling,僅執行帶方法調用次數和循環回邊執行次數 profiling 的 C1 編譯;
第 3 層:也稱為 C1 編譯,執行所有帶 Profiling 的 C1 編譯;
第 4 層:可稱為 C2 編譯,也是將字節碼編譯為本地代碼,但是會啟用一些編譯耗時較長的優化,甚至會根據性能監控信息進行一些不可靠的激進優化。
通常情況下,C2 的執行效率比 C1 高出30%以上。
在 Java8 中,默認開啟分層編譯。如果隻想開啟 C2,可以關閉分層編譯(-XX:-TieredCompilation
),如果隻想用 C1,可以在打開分層編譯的同時,使用參數:-XX:TieredStopAtLevel=1
。
通過 java -version
命令行可以查看到當前虛擬機解析字節碼的方式,mixed mode
表示既有解釋模式也有即是編譯模式。
java version "1.8.0_261" Java(TM) SE Runtime Environment (build 1.8.0_261-b12) Java HotSpot(TM) 64-Bit Server VM (build 25.261-b12, mixed mode)
mixed mode
代表是默認的混合編譯模式,除瞭這種模式外,我們還可以使用-Xint
參數強制虛擬機運行於隻有解釋器的編譯模式下;也可以使用參數-Xcomp
強制虛擬機運行於隻有 JIT 的編譯模式下。
僅使用解釋模式
通過命令java -Xint -version
設置僅使用解釋模式,interpreted mode
表示解釋模式。
java version "1.8.0_261" Java(TM) SE Runtime Environment (build 1.8.0_261-b12) Java HotSpot(TM) 64-Bit Server VM (build 25.261-b12, interpreted mode)
僅使用編譯模式
通過命令java -Xcomp -version
設置僅使用編譯模式,compiled mode
表示編譯模式。在編譯模式下,程序啟動能感覺到明顯的卡頓。
java version "1.8.0_261" Java(TM) SE Runtime Environment (build 1.8.0_261-b12) Java HotSpot(TM) 64-Bit Server VM (build 25.261-b12, compiled mode)
四、小結
通過對Java虛擬機啟動過程的解析,特別是即時編譯
環節的理解,Java應用運行並不慢。當應用中熱點代碼普遍被編譯成匯編指令(二進制可執行命令)存放於內存中時,可近似達到C語言原生程序的運行速度。
隨著算力與內存成本日漸降低,通過空間復雜度置換時間復雜度的策略顯然是合理的,使用Java語言編寫需求萬千變化的應用是第一選擇:既有跨平臺、內存安全、框架生態豐富的優點,也在運行效率方面積極改善,這種折中選擇與市場反饋保持一致。
到此這篇關於Java虛擬機啟動過程解析的文章就介紹到這瞭,更多相關Java虛擬機啟動內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- 詳解Java的編譯執行與解釋執行
- 2020macOS Big Sur配置Java開發環境之jdk安裝過程
- Java10新特性解讀
- 使用java -jar修改SpringBoot中application.properties的配置項
- 解決Elasticsearch因jdk版本問題啟動失敗的問題