一、JVM 架構基礎
JVM 程式啟動時,ClassLoader 會將需要的所有類載入到記憶體,主要分為以下三步:
- Bootstrap Class: 核心類庫,由 “Bootstrap Class Loader”負責載入, 例如基礎的執行時類庫 JRE\lib\rt.jar。
- Extension Class: java.ext.dirs 路徑下的類,由 ExtClassLoader 負責載入。在實際開發中,如果需要新增額外的類庫,通常放置於此位置。
- Application Class: 實際應用包含的類,由 AppClassLoader 負責載入。
二、JVM 預熱是指什麼?
類載入過程完畢後,所有需要的類會進入 JVM cache (native code) ,這樣就可以被快速的實時訪問。當然,還有許多其它與JVM啟動無關的類此時並未被載入。
當應用的第一個請求到來,會觸發邏輯相關類的第一次載入,此過程會有一定的耗時,會影響第一次呼叫的實時響應。這主要是因為JVM的懶載入及JIT機制。因此對於低延遲應用,必須採用特定的策略來處理第一次的預載入邏輯,以保障第一次的請求的快速響應。此過程,我們稱之為 JVM 的預熱。
三、Tiered Compilation
JVM會將使用頻率較高的方法放入本地快取。以達到快速呼叫響應的目的。基於此,我們可以通過在應用啟動之初,強制載入我們預先認知的高頻方法。設定引數包括如下:
-XX:CompileThreshold -XX:TieredCompilation
通常虛擬機器會通過直譯器來收集反饋到編譯器的方法呼叫資訊。
四、自定義實現
基於上一小節所述,我們可以額外實現特定邏輯來進行特定方法的多次呼叫(-XX:CompileThreshold),以觸發JVM的編譯。如下示例:
首先,我們定義一個包含基礎方法的類:
public class Dummy { public void m() { } }
其次,我們建立一個載入類,在其內部新增靜態方法,迴圈100000次重複生成Dummy物件,並呼叫其方法:
public class ManualClassLoader { protected static void load() { for (int i = 0; i < 100000; i++) { Dummy dummy = new Dummy(); dummy.m(); } } }
現在我們使用如下過程,測試效能:
public class MainApplication { static { long start = System.nanoTime(); ManualClassLoader.load(); long end = System.nanoTime(); System.out.println("Warm Up time : " + (end - start)); } public static void main(String[] args) { long start = System.nanoTime(); ManualClassLoader.load(); long end = System.nanoTime(); System.out.println("Total time taken : " + (end - start)); } }
如下為測試結果:
預熱之後 |
未預熱 |
差別(%) |
1220056 |
8903640 |
730 |
1083797 |
13609530 |
1256 |
1026025 |
9283837 |
905 |
1024047 |
7234871 |
706 |
868782 |
9146180 |
1053 |
預熱之後的效能明顯好於未預熱狀態下的呼叫。
當然,這裡只是一個簡單的示例測試,具體到實際的應用中,還需要考慮特定的業務邏輯需求。
五、工具
通常用於基準測試,基本使用如下:
依賴 pom.xml:
<dependency> <groupId>org.openjdk.jmh</groupId> <artifactId>jmh-core</artifactId> <version>1.19</version> </dependency> <dependency> <groupId>org.openjdk.jmh</groupId> <artifactId>jmh-generator-annprocess</artifactId> <version>1.19</version> </dependency>
maven庫連線: Central Maven Repository。
定義預熱處理方法,並新增@Benchmark註解:
@Benchmark public void init() { //code todo }
將需要預熱的業務邏輯放置於預熱處理方法內。