jvm 即時編譯器
在jdk7以后出現clinet編譯器和server編譯器,默認為client模式運行,jdk8以后被選為默認的混合模式。一般的情況下是我們代碼是在
在jdk7以后出現clinet編譯器和server編譯器,默認為client模式運行,jdk8以后被選為默認的混合模式。一般的情況下是我們代碼是在解釋執行下,進行運行,當到達一定閾值觸發即時編譯。
分層編譯
對響應速度快,啟動速度快有要求的情況下可以使用client模式(c1),對吞吐峰值有要求,但是可以忍受一定延遲性的需求,可以使用server模式(c2)。jdk8以后使用折中的方式編譯代碼,即有c1的響應速度,又可以有c2的吞吐,性能峰值。
在分層編譯中,編譯器將執行狀態分為5個層次:

0.解釋執行。
1.執行不帶profile的c1代碼。
2.執行僅僅只有循環回邊和方法調用次數profile的c1代碼。
3.執行全量的profile的c1代碼。
4.執行c2代碼。
注:性能上2層,3層性能依次降低。
在上圖中,1和4為編譯終止態。
在c1繁忙的情況,會直接跳過c1的編譯過程,直接進行c2編譯
在c2繁忙的情況,c1會經過2,3層編譯之后,再由c2編譯。
觸發條件
jvm根據循環回邊次數和方法調用次數總和來判斷是否會觸發即時編譯
-XX:CompileThreshold
在不啟動分層編譯下:c1為1500觸發編譯,c2為10000觸發編譯
在啟動分層編譯下:會動調整次數,觸發編譯:
系數的計算方法為:ns = queue_size_X / (TierXLoadFeedback * compiler_count_X) + 1nn閾值設定 -XX:CompileThreshold = 1500n觸發調用條件ncts = 1500 * snn其中X是執行層次,可取3或者4;nqueue_size_X是執行層次為X的待編譯方法的數目;nTierXLoadFeedback是預設好的參數,其中Tier3LoadFeedback為5,Tier4LoadFeedback為3;ncompiler_count_X是層次X的編譯線程數目,會根據cpu核心數來自動指定,c1:c2 比例為1:2
優化
- profile 優化
profile是指在程序運行期間,動態的收集數據運行狀態的數據。
- 基于分支profile的優化
public void test(int k){n int result = 0;n if(k == 0){n result = 1;n }else if(k == 1){n result = 2;n }n}
在上面的代碼里,虛擬機會不斷收集調用這個profile信息,如果這這方法一直只進行一個分支的判斷,虛擬機就會推斷,程序不會走另一個分支,即時編譯器就會省略掉一個,只對一個分支進行編譯為機器碼。
例如:
public void test(int k){n result = 1;n}
- 基于類型profile的優化
與分支優化相似,直接上代碼
public void test(Object obj){n if(obj instanceof Dog){n //this is dogn }n if(obj instanceof Cat){n //this.catn }n}
編譯器同樣會判斷是不是只走了一個邏輯,來進行代碼優化編譯
- 方法內聯
方法內聯:在程序執行期間,編譯器會把被調用方法里的邏輯,直接納入到調用方法中。
內聯條件:強制內聯,IR圖,方法大小,調用次數,熱點代碼。
在jvm準備階段,會把符號引用轉化為實際引用,編譯器會根據這些引用將方法納入進來。
但是對虛方法是運行期間才能確定方法調用,需要進行去虛化處理。
jvm方法調用 - 搜索結果 - 知乎條件去虛化:進行類型比較,是將實現類一個個全部放入代碼段中。
public interface Annimal{n void apply()n}npublic class Dog implements Annimal{n public void apply(){n //TODOn }n}npublic class Cat implements Annimal{n public void apply(){n //TODOn }n}n//轉換前npublic void add(Annimal a){n a.apply(){n }n}n//轉換后npublic void add(Annimal a){n if(a.getClass() == Cat.class){n n }n if(a.getClass() == Dog.class){nn }n}
完全去虛化:在全類中查找,如果只有一個類型,那么直接內聯,由于無法判斷這一次加載的類是否字類實現,需要在每次加載類的時候,都要進行判斷。
- 逃逸分析
編譯器根據這段代碼或者這個對象是不是會被未知的引用所引用,如果無法判斷,可能會被未知引用,那么就會判斷為逃逸分析,不會對它進行優化。
標量替換:在java內存,對象并不會存儲在棧上面,如果這段代碼,為熱點代碼,編譯器會把對象中的內容,直接替換為值替代,減少壓棧,彈棧,堆內存的大小。
鎖消除:
synchronized(new object){}
這種代碼,每一次加鎖都會創建一個單獨鎖,這和沒有加鎖一樣,編譯器會去掉這段邏輯
分支優化:
public void test(boolean f){n Object obj = new Object();n if (f){n System.out.println(f);n }n}
如果有99% (大概值)的執行邏輯不會執行到這里,就會判斷不會逃逸,會對齊進行優化為
public void test(boolean f){n if (f){n Object obj = new Object();n System.out.println(f);n }n}
去優化
OSR去優化:在即時編譯中,在對循環回邊進行的統計的時候,會在執行代碼出,插入trap函數,當執行機器碼的時候,由一段邏輯,與即時編譯優化判斷,不符合的時候,虛擬機會回退會解釋執行,而這些trap函數,就是回歸解釋執行,執行未優化之前代碼。
上一篇:美即好效應









