JVM性能魔术技巧
HotSpot是我們眾所周知和喜愛的JVM,是Java和Scala汁流淌的大腦。 多年來,許多工程師對(duì)其進(jìn)行了改進(jìn)和調(diào)整,并且在每次迭代中,其代碼執(zhí)行的速度和效率都接近本機(jī)編譯代碼。
JIT(“即時(shí)”)編譯器是其核心。 該組件的唯一目的是使您的代碼快速運(yùn)行,這是HotSpot如此受歡迎和成功的原因之一。
JIT編譯器實(shí)際上是做什么的?
在執(zhí)行代碼時(shí),JVM會(huì)收集有關(guān)其行為的信息。 一旦收集了有關(guān)熱方法的足夠統(tǒng)計(jì)信息(默認(rèn)閾值為10K調(diào)用),編譯器就會(huì)啟動(dòng),并將該方法的與平臺(tái)無關(guān)的“慢”字節(jié)碼轉(zhuǎn)換為自身的優(yōu)化,精簡(jiǎn),平均編譯版本。
一些優(yōu)化是顯而易見的:簡(jiǎn)單的方法內(nèi)聯(lián),清除無效代碼,用本機(jī)數(shù)學(xué)運(yùn)算替換庫調(diào)用等。請(qǐng)注意,JIT編譯器不會(huì)就此停止。 這是它執(zhí)行的一些更有趣的優(yōu)化:
分而治之
您使用以下模式多少次:
StringBuilder sb = new StringBuilder("Ingredients: ");for (int i = 0; i < ingredients.length; i++) {if (i > 0) {sb.append(", ");}sb.append(ingredients[i]); }return sb.toString();也許這個(gè):
boolean nemoFound = false;for (int i = 0; i < fish.length; i++) {String curFish = fish[i];if (!nemoFound) {if (curFish.equals("Nemo")) {System.out.println("Nemo! There you are!");nemoFound = true;continue;}}if (nemoFound) {System.out.println("We already found Nemo!");} else {System.out.println("We still haven't found Nemo : (");} }這兩個(gè)循環(huán)的共同點(diǎn)是,在這兩種情況下,循環(huán)都會(huì)做一件事一段時(shí)間,然后從某個(gè)角度開始做另一件事。 編譯器可以發(fā)現(xiàn)這些模式,并將循環(huán)分成多個(gè)案例,或“剝離”幾次迭代。
讓我們以第一個(gè)循環(huán)為例。 if (i > 0)行在一次迭代中從false開始,并且從那一點(diǎn)開始始終計(jì)算為true 。 為何每次都要檢查狀況? 編譯器將編譯該代碼,就像這樣編寫:
StringBuilder sb = new StringBuilder("Ingredients: ");if (ingredients.length > 0) {sb.append(ingredients[0]);for (int i = 1; i < ingredients.length; i++) {sb.append(", ");sb.append(ingredients[i]);} }return sb.toString();這樣,即使某些代碼可能在進(jìn)程中重復(fù),冗余的if (i > 0)也將被刪除,因?yàn)樗俣染褪撬娜俊?
生活在邊緣
空檢查是一丁點(diǎn)的。 有時(shí)null對(duì)于我們的引用是有效值(例如,指示缺少值或錯(cuò)誤),但有時(shí)為了安全起見,我們添加了null檢查。
其中一些檢查可能永遠(yuǎn)不會(huì)失敗(就此而言,null表示失敗)。 一個(gè)經(jīng)典的示例將包含一個(gè)斷言,如下所示:
public static String l33tify(String phrase) {if (phrase == null) {throw new IllegalArgumentException("phrase must not be null");}return phrase.replace('e', '3'); }如果您的代碼運(yùn)行良好,并且從未將null作為l33tify的參數(shù)l33tify ,則斷言將永遠(yuǎn)不會(huì)失敗。
在多次執(zhí)行此代碼而沒有進(jìn)入if語句的主體之后,JIT編譯器可能會(huì)樂觀地認(rèn)為此檢查很有可能是不必要的。 然后它將繼續(xù)編譯該方法,將檢查全部丟棄,就好像是這樣寫的:
public static String l33tify(String phrase) {return phrase.replace('e', '3'); }這可以顯著提高性能,這在大多數(shù)情況下可能是純粹的勝利。
但是,如果那個(gè)幸福道路的假設(shè)最終被證明是錯(cuò)誤的呢?
由于JVM現(xiàn)在正在執(zhí)行本機(jī)已編譯的代碼,因此null引用不會(huì)導(dǎo)致模糊的NullPointerException ,而是導(dǎo)致實(shí)際的,苛刻的內(nèi)存訪問沖突。 JVM是它的低級(jí)生物,它將攔截產(chǎn)生的分段錯(cuò)誤,進(jìn)行恢復(fù),并進(jìn)行反優(yōu)化處理-編譯器不能再假設(shè)null檢查是多余的:它重新編譯該方法,這次使用null檢查。
虛擬精神錯(cuò)亂
JVM的JIT編譯器與其他靜態(tài)編譯器(如C ++編譯器)之間的主要區(qū)別之一是,JIT編譯器具有動(dòng)態(tài)運(yùn)行時(shí)數(shù)據(jù),決策時(shí)可以依靠該數(shù)據(jù)來運(yùn)行。
方法內(nèi)聯(lián)是一種常見的優(yōu)化方法,在該方法中,編譯器采用一個(gè)完整的方法并將其代碼插入另一個(gè)程序中,以避免調(diào)用方法。 在處理虛擬方法調(diào)用(或動(dòng)態(tài)調(diào)度 )時(shí),這會(huì)有些棘手。
以以下代碼為例:
public class Main {public static void perform(Song s) {s.sing();} }public interface Song { void sing(); }public class GangnamStyle implements Song {@Overridepublic void sing() {System.out.println("Oppan gangnam style!");} }public class Baby implements Song {@Overridepublic void sing() {System.out.println("And I was like baby, baby, baby, oh");} }// More implementations here該方法perform可能被執(zhí)行數(shù)百萬次,每一次方法的調(diào)用sing發(fā)生。 調(diào)用是昂貴的,尤其是諸如此類的調(diào)用,因?yàn)檎{(diào)用需要根據(jù)s的運(yùn)行時(shí)類型每次動(dòng)態(tài)選擇要執(zhí)行的實(shí)際代碼。 在這一點(diǎn)上,內(nèi)聯(lián)似乎是一個(gè)遙不可及的夢(mèng)想,不是嗎?
不必要! 執(zhí)行后, perform幾千次,編譯器可能會(huì)決定,根據(jù)其收集的統(tǒng)計(jì)數(shù)據(jù),該調(diào)用的95%的目標(biāo)的一個(gè)實(shí)例GangnamStyle 。 在這些情況下,HotSpot JIT可以執(zhí)行樂觀優(yōu)化,以消除虛擬的sing調(diào)用。 換句話說,編譯器將為這些代碼生成本機(jī)代碼:
public static void perform(Song s) {if (s fastnativeinstanceof GangnamStyle) {System.out.println("Oppan gangnam style!");} else {s.sing();} }由于此優(yōu)化依賴于運(yùn)行時(shí)信息,因此即使它是多態(tài)的,它也可以消除大多數(shù)sing調(diào)用。
JIT編譯器還有很多技巧,但是這些只是一些技巧,可讓您了解當(dāng)我們的代碼由JVM執(zhí)行和優(yōu)化時(shí)的幕后故事。
我是否能幫助?
JIT編譯器是面向簡(jiǎn)單人員的編譯器; 它旨在優(yōu)化簡(jiǎn)單的編寫,并搜索出現(xiàn)在日常標(biāo)準(zhǔn)代碼中的模式。 幫助您的編譯器的最好方法是不要太努力地幫助它-只需編寫代碼即可。
翻譯自: https://www.javacodegeeks.com/2013/06/jvm-performance-magic-tricks.html
總結(jié)
- 上一篇: 重启wifi后怎么设置(重启wifi后怎
- 下一篇: 现代网络开发