JIT與AOT

zhengbiyu發表於2024-07-04

JIT

JIT(Just-in-Time,實時編譯)一直是Java語言的靈魂特性之一,HotSpot JVM中整合了兩種JIT編譯器,Client Compiler和Server Compiler,它們的作用也不同。Client Compiler注重啟動速度和區域性的最佳化,Server Compiler則更加關注全域性的最佳化,效能會更好,但由於會進行更多的全域性分析,所以啟動速度會變慢。兩種編譯器有著不同的應用場景,在虛擬機器中同時發揮作用。而隨著時間的發展,不論是Client Compiler還是Server Compiler都發展出了各具特色的實現,如 C1、C2、Graal Compiler等,你可以在JVM啟動引數中選擇自己所需的JIT編譯器實現。

JIT與AOT的區別

提前編譯是相對於即時編譯的概念,提前編譯能帶來的最大好處是Java虛擬機器載入這些已經預編譯成二進位制庫之後就能夠直接呼叫,而無須再等待即時編譯器在執行時將其編譯成二進位制機器碼。理論上,提前編譯可以滅少即時編譯帶來的預熱時間,減少Java應用長期給人帶來的“第一次執行慢"的不良體驗,可以放心地進行很多全程式的分析行為,可以使用時間壓力更大的最佳化措施。但是提前編譯的壞處也很明顯,它破壞了Java"—次編寫,到處執行"的承諾,必須為每個不同的硬體、作業系統去編譯對應的發行包;也顯著降低了Java連結過程的動態性,必須要求載入的程式碼在編譯期就是全部已知的,而不能在執行期才確定,否則就只能捨棄掉己經提前編譯好的版本,退回到原來的即時編譯執行狀態。

AOT的優點

在程式執行前編譯,可以避免在執行時的編譯效能消耗和記憶體消耗
可以在程式執行初期就達到最高效能,程式啟動速度快
執行產物只有機器碼,打包體積小

AOT的缺點

由於是靜態提前編譯,不能根據硬體情況或程式執行情況擇優選擇機器指令序列,理論峰值效能不如JIT
沒有動態能力,使用到反射、動態編譯需要額外處理。
同一份產物不能跨平臺執行。

Native Image:原理與限制

Native Image的輸入是整個應用的所有元件,包括應用本身的程式碼、各種依賴的庫、JDK庫、以及SVM;首先會進行整個應用的初始化,也就是程式碼的靜態分析,這個分析過程有點類似GC中的“可達性分析”,會講程式執行過程中將所有可達的程式碼、變數、物件生成一個快照,最終打包成一個可執行的Native Image。

一個完整的Native Image包含兩個部分,一部分稱為 Text Section,即使用者程式碼編譯成的機器程式碼;另一部分稱為 Data Section,儲存了應用啟動後堆區記憶體中各種物件的快照。
例如反射、代理,要如何進行靜態分析呢?很顯然,這兩者之間是存在衝突的,因此Native Image設定了一個名為“Closed World”的假設作為靜態分析的基本前提。

這個基本前提包含三個要求,對應的也就是目前Native Image存在的三個限制:

Points-to分析的時候,需要接受完整的位元組碼作為輸入(即專案中所有用到的class的位元組碼都需要獲取的到)。
=> 在執行期動態生成或者是動態獲取位元組碼的程式,無法構建成 Native Image。

Java的動態特性,包括反射、JNI、代理,都需要透過配置檔案在構建前實現宣告好。
=> 無法提前宣告動態特性使用範圍的程式,無法構建成Native Image (例如,根據使用者輸入的一個引數反射去呼叫某個方法)。

在整個執行過程中,程式不會再載入任何新的class。
=> 在執行期間執行動態編譯,或者是自定義Classloader動態裝載類的程式,無法構建成Native Image。

Spring Native

相關文章