參考:《分散式 Java 應用:基礎與實踐》、《深入理解 Java 虛擬機(第二版)》
| 模式 | 說明 | 強制參數 |
|---|---|---|
| 解釋執行 | 運行期逐條解釋字節碼並執行 | -Xint |
| JIT 編譯執行 | 運行期將熱點字節碼編譯為機器碼直接執行 | -Xcomp |
預設選擇:32 位元機器預設 C1;64 位元且 CPU>2 核、記憶體>2GB 則預設 C2。可用 -client / -server 強制指定。
HotSpot 混合模式:對執行頻率高的「熱點代碼」進行 JIT 編譯,其餘繼續解釋執行,平衡啟動速度與執行效能。
參考《深入理解 Java 虛擬機》第 3 章類檔案結構與 javap 反編譯,以 inc() 方法為例,解釋器基於棧架構逐條執行字節碼指令(iload、iinc、istore 等)。
| 計數器 | C1 (Client) | C2 (Server) | 配置參數 |
|---|---|---|---|
| 方法調用計數器 | 1,500 | 10,000 | -XX:CompileThreshold |
| 回邊計數器 (迴圈執行次數) |
13,995 | 10,700 | -XX:OnStackReplacePercent |
半衰期機制:計數器統計的是一段時間(半衰周期)內的次數。若半衰期內未達閾值,計數值減半。可用 -XX:-UseCounterDecay 關閉半衰,-XX:CounterHalfLifeTime 設定周期。
「少量高性價比優化」,主要包含:
// 原始碼
static class B { int value; final int get() { return value; } }
public void foo() { y = b.get(); /* do something */ z = b.get(); sum = y + z; }
// 1. 方法內聯 (get() ≤ 35 bytes,-XX:MaxInlineSize)
public void foo() { y = b.value; /* do something */ z = b.value; sum = y + z; }
// 2. 冗余消除 (假設 do something 未修改 b.value)
public void foo() { y = b.value; /* do something */ z = y; sum = y + z; }
// 3. 複製傳播 (z 僅用於 sum 計算)
public void foo() { y = b.value; /* do something */ y = y; sum = y + y; }
// 4. 無用代碼消除
public void foo() { y = b.value; /* do something */ sum = y + y; }
// 假設 Animal 只有 Cat 一個實現類
public void methodA(Animal animal) { animal.eat(); }
// CHA 分析後去虛擬化
public void methodA(Animal animal) { System.out.println("cat eat fish"); }
// 運行期載入 Dog 實現類 → 逆優化:回退至解釋執行
逆優化:當編譯假設失效(如新增子類),機器碼失效,回退解釋執行。
啟用:-XX:+DoEscapeAnalysis
判斷方法內變數是否被外部執行緒/方法讀取,若不逃逸則進行:
| 優化 | 啟用參數 | 效果 |
|---|---|---|
| 標量替換 | -XX:+EliminateAllocations |
物件打散為標量變數,節省記憶體、避免引用查找 |
| 棧上分配 | 自動 | 非逃逸物件分配在棧上,方法結束自動銷毀,減輕 GC 壓力 |
| 同步消除 | -XX:+EliminateLocks |
非逃逸物件的同步塊移除 |
Point point = new Point(1, 2);
System.out.println("x:" + point.x + ", y:" + point.y);
// 後續無任何代碼訪問 point
// ↓ 標量替換後
int x = 1; int y = 2;
System.out.println("x:" + x + ", y:" + y);
synchronized(cat) { /* do xxx */ }
// 若 cat 不逃逸出當前執行緒
// ↓ 同步消除後
/* do xxx */
| 特性 | 解釋器 | JIT 編譯器 |
|---|---|---|
| 啟動速度 | 快 | 慢(需編譯暖機) |
| 記憶體佔用 | 少(無機器碼) | 多(需儲存編譯結果) |
| 長期執行效能 | 穩定較低 | 熱點代碼極高 |
| 運行期資訊收集 | 無 | 有(指導優化決策) |
為何不用 AOT(預先編譯)? 除上述優點外,JIT 關鍵優勢在於運行期收集 Profile 資訊,依據實際執行熱點與型別分佈進行針對性優化(如去虛擬化、內聯決策),這正是 AOT 無法做到的。