1. 程式人生 > >淺析Hotspot JIT編譯

淺析Hotspot JIT編譯

1. 什麼是編譯?

     像java,C語言這些高階程式語言,計算機並不能直接執行.計算機認識的是彙編或者機器語言,是可以直接由計算機直接執行的.所以從高階語言轉化為計算機認識的低階語言的過程叫做編譯.
這裡寫圖片描述
     上圖是javac編譯的過程,編譯完成後會生成位元組碼,也就是.class檔案.之後java命令在將.class檔案翻譯為機器語言.編譯的過程也可以成為前端編譯,翻譯也可以稱為後端編譯.

2. 什麼是JIT?

     JIT 是 just in time 的縮寫, 也就是即時編譯編譯器。上面我們說過編譯的流程, javac 將程式原始碼編譯,轉換成 java 位元組碼,JVM 通過解釋位元組碼將其翻譯成對應的機器指令,逐條讀入,逐條解釋翻譯。這樣編譯->在翻譯,其執行速度肯定會比直接執行二進位制位元組碼程式要慢。所以為了提高執行速度,引入了 JIT 技術,在執行時 JIT 會把翻譯過的機器碼儲存起來,以備下次使用。

3. JIT怎麼工作的?

     我們知道了JIT是為了提高程式的執行速度,那麼具體的過程是怎麼樣的呢?從上文可知大致流程是將翻譯過的機器碼儲存起來,第二次直接使用,省去了重複編譯的過程.

3.1 JIT的工作流程

這裡寫圖片描述
如圖,圖中有一個Hot判斷.JVM將重複出現很多次數,迴圈,重複呼叫的方法的位元組碼給儲存起來.這種程式碼,叫熱點程式碼,檢測出熱點程式碼的過程叫熱點檢測.這裡涉及到兩個問題,一個是JIT怎麼判斷位元組碼是否是熱點程式碼,另一個是這些被儲存的程式碼放在哪裡?

3.1.1 JIT的熱點檢測

這裡引用H神的文章片段:
1、基於取樣的方式探測(Sample Based Hot Spot Detection) :週期性檢測各個執行緒的棧頂,發現某個方法經常出險在棧頂,就認為是熱點方法。好處就是簡單,缺點就是無法精確確認一個方法的熱度。容易受執行緒阻塞或別的原因干擾熱點探測。

2、基於計數器的熱點探測(Counter Based Hot Spot Detection)。採用這種方法的虛擬機器會為每個方法,甚至是程式碼塊建立計數器,統計方法的執行次數,某個方法超過閥值就認為是熱點方法,觸發JIT編譯。

在HotSpot虛擬機器中使用的是第二種——基於計數器的熱點探測方法,因此它為每個方法準備了兩個計數器:方法呼叫計數器和回邊計數器。

方法計數器。顧名思義,就是記錄一個方法被呼叫次數的計數器。
回邊計數器。是記錄方法中的for或者while的執行次數的計數器。

3.1.2 JIT的程式碼快取

     JIT 編譯器在執行程式時有兩種編譯模式可以選擇.引數:-client 或者 -server.client模式是一種輕量級的編譯器,叫做C1編譯器,server模式下是更重一些的編譯器,叫做C2編譯器. Intel 系列機器,client 編譯器模式下程式碼快取大小起始於 160KB,server 編譯器模式下程式碼快取大小則起始於 2496KB.
     當需要提高程式碼快取時,一個通常的做法是將程式碼快取變成預設大小的兩倍或四倍。

4. 怎麼檢視編譯的過程

     OpenJDK HotSpot VM提供了一個引數命令:-XX:+PrintCompilation,它會報告什麼時候程式碼快取滿了,以及什麼時候編譯停止了。
日誌形式:
timestamp compilation-id flags tiered-compilation-level class:method <@ osr_bci> code-size <deoptimization>
timestamp:JVM開始啟動到此時的時間;
compilation_id:內部任務的id;
flags:
%: is_osr_method (是否osr方法針對OSR 棧上替換 方法表明位元組碼)
s: is_synchronized(是否同步的)
!: has_exception_handler(有異常處理器)
b: is_blocking(是否堵塞)
n: is_native(是否原生);
tiered-compilation(分層的編譯器) 表示當開啟了分層編譯時的編譯層;
Method(方法) 將用以下格式表示類和方法 類名::方法;
@osr_bci(osr位元組碼索引) 是OSR中的位元組碼索引;
code-size(程式碼大小) 位元組碼總大小;
deoptimization(逆優化)表示一個方法是否是逆優化,以及不會被呼叫或是殭屍方法.