即時編譯回顧
HotSpot 虛擬機執(zhí)行 Java 程序時身辨,先通過解釋器對代碼解釋執(zhí)行,發(fā)現(xiàn)某個方法或代碼塊執(zhí)行比較頻繁后,對熱點代碼進行編譯铺敌,編譯后生成與本地平臺相關(guān)的機器碼默终,再去執(zhí)行機器碼獲得較高的運行效率椅棺。必要時,也會通過逆優(yōu)化從即時編譯回到解釋執(zhí)行齐蔽,如編譯器遇到罕見陷阱的情況两疚。
在 Java 虛擬機規(guī)范中,并未要求虛擬機必須實現(xiàn)即時編譯含滴,但即時編譯在主流的虛擬機中都有實現(xiàn)鬼雀,后文所有討論都基于 HotSpot 虛擬機。
即時編譯器
HotSpot 虛擬機中有兩個即時編譯器蛙吏,分別為 Client Compiler 和 Server Compiler源哩,簡稱為 C1 編譯器和 C2 編譯器鞋吉。C1 編譯器占用內(nèi)存小,進行局部優(yōu)化励烦,代碼執(zhí)行效率比 C2 編譯器低谓着,適用于客戶端應(yīng)用; C2 編譯器占用內(nèi)存大坛掠,進行全局優(yōu)化赊锚,代碼執(zhí)行效率比 C1 編譯器高,適用于服務(wù)端應(yīng)用屉栓。在 JDK 1.7 以后的 HotSpot 虛擬機中舷蒲,采用了被稱為分層編譯的策略,啟動時解釋執(zhí)行提高啟動速度友多,然后 C1 編譯獲取較好的編譯速度牲平,再 C2 編譯獲取較好的編譯質(zhì)量。
默認情況下域滥,虛擬機會自動選擇合適的編譯器與解釋器一起配合工作纵柿,用戶也可以在啟動參數(shù)里使用 “-client” 或 “-server” 來強制要求虛擬機使用某一種編譯器。編譯器與解釋器一起配合工作的模式启绰,被稱為混合模式昂儒。用戶也可以在啟動參數(shù)里使用 “-Xint” 來要求虛擬機只使用解釋器,使用 “-Xcomp” 來要求虛擬機只使用編譯器委可。
被多次調(diào)用的方法和被多次執(zhí)行的循環(huán)體渊跋,就是即時編譯器關(guān)注的熱點代碼。在 HotSpot 中着倾,采用了基于計數(shù)器的熱點探測技術(shù)刹枉,為每個方法定義了兩個計數(shù)器:方法調(diào)用計數(shù)器、回邊計數(shù)器屈呕。
默認情況下微宝,如果一段時間內(nèi)方法調(diào)用計數(shù)器的值沒有超過虛擬機設(shè)置的閾值,則在垃圾回收時計數(shù)器會熱度衰減虎眨,數(shù)值減少 1/2蟋软,此時間范圍又被稱為半衰期。所以方法調(diào)用計數(shù)器中存儲的實際上并不是方法調(diào)用的絕對次數(shù)嗽桩。用戶可以調(diào)整半衰期的值岳守,甚至可以關(guān)閉熱度衰減。
回邊計數(shù)器則統(tǒng)計了循環(huán)體執(zhí)行的絕對次數(shù)碌冶,它的閾值可以由方法調(diào)用計數(shù)器的閾值計算出來湿痢。如果回邊計數(shù)器發(fā)生溢出,也會把方法調(diào)用計數(shù)器調(diào)整為溢出。
調(diào)用方法時譬重,如果兩個計數(shù)器之和超過了方法調(diào)用計數(shù)器的閾值拒逮,就會提交方法的編譯請求。循環(huán)體執(zhí)行時臀规,如果兩個計數(shù)器之和超過了回邊計數(shù)器的閾值滩援,也是編譯代碼塊所在的方法,因為此時方法正在執(zhí)行中塔嬉,又被稱為棧上替換玩徊,即替換方法時方法的棧幀還在棧上。
默認情況下谨究,編譯器在后臺進行編譯恩袱,即調(diào)用熱點代碼發(fā)現(xiàn)需要編譯時,先以解釋方式繼續(xù)執(zhí)行胶哲,向編譯器發(fā)送編譯請求畔塔,編譯結(jié)束后下次調(diào)用時才使用編譯的本地代碼。用戶也可以通過啟動參數(shù)來要求虛擬機禁止后臺編譯纪吮,編譯結(jié)束前熱點代碼的執(zhí)行處于阻塞狀態(tài)。
優(yōu)化技術(shù)
HotSpot 虛擬機使用了很多種優(yōu)化技術(shù)萎胰,這里只簡單介紹其中的幾種碾盟,完整的優(yōu)化技術(shù)介紹可以參考官網(wǎng)內(nèi)容。
公共子表達式消除:
如果一個表達式已經(jīng)進行過計算技竟,并且在下次用到之前依賴的變量沒有變化冰肴,即表達式的計算結(jié)果不會發(fā)生變化,則在下次使用這個表達式時直接使用計算的結(jié)果榔组。
數(shù)組邊界檢查消除:
在 Java 中訪問數(shù)組時熙尉,會自動進行邊界檢查來防止數(shù)組下標越界。但是對于某些情況并不需要每次訪問都去檢查搓扯,如在一個循環(huán)中遍歷數(shù)組元素检痰,如果虛擬機能夠確定下標不會發(fā)生越界并且優(yōu)化確實能夠提高運行速度,則虛擬機會去除每次訪問的下標檢查锨推。
方法內(nèi)聯(lián):
對于可以內(nèi)聯(lián)的方法铅歼,直接復(fù)制到調(diào)用者代碼中,減少方法調(diào)用次數(shù)和性能消耗换可。
逃逸分析:
方法中定義的一個對象椎椰,如果會被其他方法訪問則稱為方法逃逸,如果會被其他線程訪問則稱為線程逃逸沾鳄。對于不能逃逸的對象慨飘,HotSpot 虛擬機采用了棧上分配、同步消除译荞、標量替換等方法進行優(yōu)化瓤的。
每周 3 篇學(xué)習(xí)筆記或技術(shù)總結(jié)休弃,面向有一定基礎(chǔ)的 Java 程序員,內(nèi)容涉及 Java 進階堤瘤、虛擬機玫芦、MySQL、NoSQL本辐、分布式計算桥帆、開源框架等多個領(lǐng)域。關(guān)注作者或微信公眾號 backend-develop 第一時間獲取最新內(nèi)容慎皱。