jvm 性能_JVM性能魔术
jvm 性能
HotSpot是我們眾所周知和喜愛的JVM,是Java和Scala汁流淌的大腦。 多年來,許多工程師對其進行了改進和調整,并且每次迭代時,其代碼執行的速度和效率都接近于本機編譯代碼。
JIT(“及時”)編譯器是其核心。 該組件的唯一目的是使您的代碼快速運行,這是HotSpot如此受歡迎和成功的原因之一。
JIT編譯器實際上是做什么的?
在執行代碼時,JVM會收集有關其行為的信息。 一旦收集了有關熱方法的足夠統計信息(默認閾值為10K調用),編譯器就會啟動,并將該方法的與平臺無關的“慢”字節碼轉換為自身的優化,精簡,平均編譯版本。
一些優化是顯而易見的:簡單的方法內聯,清除死代碼,用本機數學運算替換庫調用等。請注意,JIT編譯器不會就此停止。 這是它執行的一些更有趣的優化:
分而治之
您使用以下模式多少次:
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();也許這個:
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 : (");} }這兩個循環的共同點是,在這兩種情況下,循環都會做一件事一段時間,然后從某個角度開始做另一件事。 編譯器可以發現這些模式,并將循環拆分為多個案例,或“剝離”多個迭代。
讓我們以第一個循環為例。 if (i > 0)行在一次迭代中以false開頭,則從那一點開始始終為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();這樣,即使某些代碼在過程中可能重復,也可以刪除多余的if (i > 0) ,因為速度就是它的全部。
生活在邊緣
空檢查是一丁點的。 有時null對于我們的引用是有效值(例如,指示缺少值或錯誤),但是有時為了安全起見,我們添加了null檢查。
其中某些檢查可能永遠不會失敗(就此而言,null表示失敗)。 一個經典的示例將包含一個斷言,如下所示:
public static String l33tify(String phrase) {if (phrase == null) {throw new IllegalArgumentException("phrase must not be null");}return phrase.replace('e', '3'); }如果您的代碼運行良好,并且從未將null作為l33tify的參數l33tify ,則斷言將永遠不會失敗。
在多次執行此代碼而沒有進入if語句的主體之后,JIT編譯器可能會樂觀地認為此檢查很有可能是不必要的。 然后,它將繼續編譯該方法,將檢查全部丟棄,就好像是這樣寫的:
public static String l33tify(String phrase) {return phrase.replace('e', '3'); }這可以顯著提高性能,這在大多數情況下可能是純粹的勝利。
但是,如果那個幸福道路的假設最終被證明是錯誤的怎么辦?
由于JVM現在正在執行本機已編譯的代碼,因此null引用不會導致模糊的NullPointerException ,而是導致實際的,苛刻的內存訪問沖突。 JVM是它的底層生物,它將攔截產生的分段錯誤,進行恢復,并進行去優化后進行后續處理-編譯器不能再假定null檢查是多余的:它重新編譯該方法,這次使用null檢查。
虛擬精神錯亂
JVM的JIT編譯器與其他靜態編譯器(例如C ++編譯器)之間的主要區別之一是,JIT編譯器具有動態運行時數據,決策時可以依賴該動態運行時數據。
方法內聯是一種常見的優化,其中編譯器采用完整的方法并將其代碼插入另一個代碼中,以避免調用方法。 在處理虛擬方法調用(或動態調度 )時,這會有些棘手。
以以下代碼為例:
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可能被執行數百萬次,每一次方法的調用sing發生。 調用是昂貴的,尤其是諸如此類的調用,因為調用需要根據s的運行時類型每次動態選擇要執行的實際代碼。 在這一點上,內聯似乎是一個遙不可及的夢想,不是嗎?
不必要! 執行后, perform幾千次,編譯器可能會決定,根據其收集的統計數據,該調用的95%的目標的一個實例GangnamStyle 。 在這些情況下,HotSpot JIT可以執行樂觀優化,以消除虛擬的sing調用。 換句話說,編譯器將為這些代碼生成本機代碼:
public static void perform(Song s) {if (s fastnativeinstanceof GangnamStyle) {System.out.println("Oppan gangnam style!");} else {s.sing();} }由于此優化依賴于運行時信息,因此即使它是多態的,它也可以消除大多數sing調用。
JIT編譯器還有很多技巧,但這些只是一些技巧,可讓您了解當我們的代碼由JVM執行和優化時的幕后故事。
我能提供幫助嗎?
JIT編譯器是面向簡單人員的編譯器; 它旨在優化簡單的編寫方式,并搜索日常標準代碼中出現的模式。 幫助您的編譯器的最佳方法是不要太努力地幫助它-只需像否則那樣編寫代碼即可。
翻譯自: https://www.javacodegeeks.com/2013/06/jvm-performance-magic-tricks.html
jvm 性能
總結
以上是生活随笔為你收集整理的jvm 性能_JVM性能魔术的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 长城是谁建的(长城最开始是从哪个朝代开始
- 下一篇: 苹果6s还原出厂设置后(苹果6s还原出厂