淺析 JIT 即時編譯技術

即時編譯回顧

HotSpot 虛擬機執行 Java 程序時,先經過解釋器對代碼解釋執行,發現某個方法或代碼塊執行比較頻繁後,對熱點代碼進行編譯,編譯後生成與本地平臺相關的機器碼,再去執行機器碼得到較高的運行效率。必要時,也會經過逆優化從即時編譯回到解釋執行,如編譯器遇到罕見陷阱的狀況。程序員

 

 在 Java 虛擬機規範中,並未要求虛擬機必須實現即時編譯,但即時編譯在主流的虛擬機中都有實現,後文全部討論都基於 HotSpot 虛擬機。後端

 

即時編譯器

HotSpot 虛擬機中有兩個即時編譯器,分別爲 Client Compiler 和 Server Compiler,簡稱爲 C1 編譯器和 C2 編譯器。C1 編譯器佔用內存小,進行局部優化,代碼執行效率比 C2 編譯器低,適用於客戶端應用; C2 編譯器佔用內存大,進行全局優化,代碼執行效率比 C1 編譯器高,適用於服務端應用。在 JDK 1.7 之後的 HotSpot 虛擬機中,採用了被稱爲分層編譯的策略,啓動時解釋執行提升啓動速度,而後 C1 編譯獲取較好的編譯速度,再 C2 編譯獲取較好的編譯質量。數組

默認狀況下,虛擬機會自動選擇合適的編譯器與解釋器一塊兒配合工做,用戶也能夠在啓動參數裏使用 「-client」 或 「-server」 來強制要求虛擬機使用某一種編譯器。編譯器與解釋器一塊兒配合工做的模式,被稱爲混合模式。用戶也能夠在啓動參數裏使用 「-Xint」 來要求虛擬機只使用解釋器,使用 「-Xcomp」 來要求虛擬機只使用編譯器。微信

被屢次調用的方法和被屢次執行的循環體,就是即時編譯器關注的熱點代碼。在 HotSpot 中,採用了基於計數器的熱點探測技術,爲每一個方法定義了兩個計數器:方法調用計數器、回邊計數器。框架

默認狀況下,若是一段時間內方法調用計數器的值沒有超過虛擬機設置的閾值,則在垃圾回收時計數器會熱度衰減,數值減小 1/2,此時間範圍又被稱爲半衰期。因此方法調用計數器中存儲的實際上並非方法調用的絕對次數。用戶能夠調整半衰期的值,甚至能夠關閉熱度衰減。分佈式

回邊計數器則統計了循環體執行的絕對次數,它的閾值能夠由方法調用計數器的閾值計算出來。若是回邊計數器發生溢出,也會把方法調用計數器調整爲溢出。post

調用方法時,若是兩個計數器之和超過了方法調用計數器的閾值,就會提交方法的編譯請求。循環體執行時,若是兩個計數器之和超過了回邊計數器的閾值,也是編譯代碼塊所在的方法,由於此時方法正在執行中,又被稱爲棧上替換,即替換方法時方法的棧幀還在棧上。性能

默認狀況下,編譯器在後臺進行編譯,即調用熱點代碼發現須要編譯時,先以解釋方式繼續執行,向編譯器發送編譯請求,編譯結束後下次調用時才使用編譯的本地代碼。用戶也能夠經過啓動參數來要求虛擬機禁止後臺編譯,編譯結束前熱點代碼的執行處於阻塞狀態。學習

 

優化技術

HotSpot 虛擬機使用了不少種優化技術,這裏只簡單介紹其中的幾種,完整的優化技術介紹能夠參考官網內容。 優化

公共子表達式消除:

若是一個表達式已經進行過計算,而且在下次用到以前依賴的變量沒有變化,即表達式的計算結果不會發生變化,則在下次使用這個表達式時直接使用計算的結果。

數組邊界檢查消除:

在 Java 中訪問數組時,會自動進行邊界檢查來防止數組下標越界。可是對於某些狀況並不須要每次訪問都去檢查,如在一個循環中遍歷數組元素,若是虛擬機可以肯定下標不會發生越界而且優化確實可以提升運行速度,則虛擬機會去除每次訪問的下標檢查。

方法內聯:

對於能夠內聯的方法,直接複製到調用者代碼中,減小方法調用次數和性能消耗。

逃逸分析:

方法中定義的一個對象,若是會被其餘方法訪問則稱爲方法逃逸,若是會被其餘線程訪問則稱爲線程逃逸。對於不能逃逸的對象,HotSpot 虛擬機採用了棧上分配、同步消除、標量替換等方法進行優化。

 

每週 3 篇學習筆記或技術總結,面向有必定基礎的 Java 程序員,內容涉及 Java 進階、虛擬機、MySQL、NoSQL、分佈式計算、開源框架等多個領域。關注做者或微信公衆號 backend-develop 第一時間獲取最新內容。

淺析 JIT 即時編譯技術 | 後端開發那點事兒

相關文章
相關標籤/搜索