JVM實用參數(二)參數分類和即時(JIT)編譯器診斷

JVM實用參數(二)參數分類和即時(JIT)編譯器診斷

做者: PATRICK PESCHLOW     原文地址    譯者:趙峯 校對:許巧輝html

在這個系列的第二部分,我來介紹一下HotSpot JVM提供的不一樣類別的參數。我一樣會討論一些關於JIT編譯器診斷的有趣參數。java

JVM 參數分類git

HotSpot JVM 提供了三類參數。第一類包括了標準參數。顧名思義,標準參數中包括功能和輸出的參數都是很穩定的,極可能在未來的JVM版本中不會改變。你能夠用java命令(或者是用 java -help)檢索出全部標準參數。咱們在第一部分中已經見到過一些標準參數,例如:-server。
github

第二類是X參數,非標準化的參數在未來的版本中可能會改變。全部的這類參數都以-X開始,而且能夠用java -X來檢索。注意,不能保證全部參數均可以被檢索出來,其中就沒有-Xcomp。web

第三類是包含XX參數(到目前爲止最多的),它們一樣不是標準的,甚至很長一段時間內不被列出來(最近,這種狀況有改變 ,咱們將在本系列的第三部分中討論它們)。然而,在實際狀況中X參數和XX參數並無什麼不一樣。X參數的功能是十分穩定的,然而不少XX參數仍在實驗當中(主要是JVM的開發者用於debugging和調優JVM自身的實現)。值的一讀的介紹非標準參數的文檔 HotSpot JVM documentation,其中明確的指出XX參數不該該在不瞭解的狀況下使用。這是真的,而且我認爲這個建議一樣適用於X參數(一樣一些標準參數也是)。無論類別是什麼,在使用參數以前應該先了解它可能產生的影響。編程

用一句話來講明XX參數的語法。全部的XX參數都以」-XX:」開始,可是隨後的語法不一樣,取決於參數的類型。安全

  • 對於布爾類型的參數,咱們有」+」或」-「,而後才設置JVM選項的實際名稱。例如,-XX:+<name>用於激活<name>選項,而-XX:-<name>用於註銷選項。
  • 對於須要非布爾值的參數,如string或者integer,咱們先寫參數的名稱,後面加上」=」,最後賦值。例如,  -XX:<name>=<value>給<name>賦值<value>。

如今讓咱們來看看JIT編譯方面的一些XX參數。併發

-XX:+PrintCompilation and -XX:+CITimeoracle

當一個Java應用運行時,很是容易查看JIT編譯工做。經過設置-XX:+PrintCompilation,咱們能夠簡單的輸出一些關於從字節碼轉化成本地代碼的編譯過程。咱們來看一個服務端VM運行的例子:app

$ java -server -XX:+PrintCompilation Benchmark
  1       java.lang.String::hashCode (64 bytes)
  2       java.lang.AbstractStringBuilder::stringSizeOfInt (21 bytes)
  3       java.lang.Integer::getChars (131 bytes)
  4       java.lang.Object::<init> (1 bytes)
---   n   java.lang.System::arraycopy (static)
  5       java.util.HashMap::indexFor (6 bytes)
  6       java.lang.Math::min (11 bytes)
  7       java.lang.String::getChars (66 bytes)
  8       java.lang.AbstractStringBuilder::append (60 bytes)
  9       java.lang.String::<init> (72 bytes)
 10       java.util.Arrays::copyOfRange (63 bytes)
 11       java.lang.StringBuilder::append (8 bytes)
 12       java.lang.AbstractStringBuilder::<init> (12 bytes)
 13       java.lang.StringBuilder::toString (17 bytes)
 14       java.lang.StringBuilder::<init> (18 bytes)
 15       java.lang.StringBuilder::append (8 bytes)
[...]
 29       java.util.regex.Matcher::reset (83 bytes)

每當一個方法被編譯,就輸出一行-XX:+PrintCompilation。每行都包含順序號(惟一的編譯任務ID)和已編譯方法的名稱和大小。所以,順序號1,表明編譯String類中的hashCode方法到原生代碼的信息。根據方法的類型和編譯任務打印額外的信息。例如,本地的包裝方法前方會有」n」參數,像上面的System::arraycopy同樣。注意這樣的方法不會包含順序號和方法佔用的大小,由於它不須要編譯爲本地代碼。一樣能夠看到被重複編譯的方法,例如StringBuilder::append順序號爲11和15。輸出在順序號29時中止 ,這代表在這個Java應用運行時總共須要編譯29個方法。

沒有官方的文檔關於-XX:+PrintCompilation,可是這個描述是對於此參數比較好的。我推薦更深刻學習一下。

JIT編譯器輸出幫助咱們理解客戶端VM與服務端VM的一些區別。用服務端VM,咱們的應用例子輸出了29行,一樣用客戶端VM,咱們會獲得55行。這看起來可能很怪,由於服務端VM應該比客戶端VM作了「更多」的編譯。然而,因爲它們各自的默認設置,服務端VM在判斷方法是否是熱點和需不須要編譯時比客戶端VM觀察方法的時間更長。所以,在使用服務端VM時,一些潛在的方法會稍後編譯就不奇怪了。

經過另外設置-XX:+CITime,咱們能夠在JVM關閉時獲得各類編譯的統計信息。讓咱們看一下一個特定部分的統計:

$ java -server -XX:+CITime Benchmark
[...]
Accumulated compiler times (for compiled methods only)
------------------------------------------------
  Total compilation time   :  0.178 s
    Standard compilation   :  0.129 s, Average : 0.004
    On stack replacement   :  0.049 s, Average : 0.024
[...]

總共用了0.178s(在29個編譯任務上)。這些,」on stack replacement」佔用了0.049s,即編譯的方法目前在堆棧上用去的時間。這種技術並非簡單的實現性能顯示,實際上它是很是重要的。沒有」on stack replacement」,方法若是要執行很長時間(好比,它們包含了一個長時間運行的循環),它們運行時將不會被它們編譯過的副本替換。

再一次,客戶端VM與服務端VM的比較是很是有趣的。客戶端VM相應的數據代表,即便有55個方法被編譯了,但這些編譯總共用了只有0.021s。服務端VM作的編譯少可是用的時間卻比客戶端VM多。這個緣由是,使用服務端VM在生成本地代碼時執行了更多的優化。

在本系列的第一部分,咱們已經學了-Xint和-Xcomp參數。結合使用-XX:+PrintCompilation和-XX:+CITime,在這兩個狀況下(校對者注,客戶端VM與服務端VM),咱們能對JIT編譯器的行爲有更好的瞭解。使用-Xint,-XX:+PrintCompilation在這兩種狀況下會產生0行輸出。一樣的,使用-XX:+CITime時,證明在編譯上沒有花費時間。如今換用-Xcomp,輸出就徹底不一樣了。在使用客戶端VM時會產生726行輸出,而後沒有更多的,這是由於每一個相關的方法都被編譯了。使用服務端VM,咱們甚至能獲得993行輸出,這告訴咱們更積極的優化被執行了。一樣,JVM 拆機(JVM teardown)時打印出的統計顯示了兩個VM的巨大不一樣。考慮服務端VM的運行:

$ java -server -Xcomp -XX:+CITime Benchmark
[...]
Accumulated compiler times (for compiled methods only)
------------------------------------------------
  Total compilation time   :  1.567 s
    Standard compilation   :  1.567 s, Average : 0.002
    On stack replacement   :  0.000 s, Average : -1.#IO
[...]

使用-Xcomp編譯用了1.567s,這是使用默認設置(即,混合模式)的10倍。一樣,應用程序的運行速度要比用混合模式的慢。相比較之下,客戶端VM使用-Xcomp編譯726個方法只用了0.208s,甚至低於使用-Xcomp的服務端VM。

補充一點,這裏沒有」on stack replacement」發生,由於每個方法在第一次調用時被編譯了。損壞的輸出「Average: -1.#IO」(正確的是:0)再一次代表了,非標準化的輸出參數不是很是可靠。

-XX:+UnlockExperimentalVMOptions

有些時候當設置一個特定的JVM參數時,JVM會在輸出「Unrecognized VM option」後終止。若是發生了這種狀況,你應該首先檢查你是否輸錯了參數。然而,若是參數輸入是正確的,而且JVM並不識別,你或許須要設置-XX:+UnlockExperimentalVMOptions 來解鎖參數。我不是很是清楚這個安全機制的做用,但我猜測這個參數若是不正確使用可能會對JVM的穩定性有影響(例如,他們可能會過多的寫入debug輸出的一些日誌文件)。

有一些參數只是在JVM開發時用,並不實際用於Java應用。若是一個參數不能被 -XX:+UnlockExperimentalVMOptions 開啓,可是你真的須要使用它,此時你能夠嘗試使用debug版本的JVM。對於Java 6 HotSpot JVM你能夠從這裏找到

-XX:+LogCompilation and -XX:+PrintOptoAssembly

若是你在一個場景中發現使用 -XX:+PrintCompilation,不可以給你足夠詳細的信息,你可使用 -XX:+LogCompilation把擴展的編譯輸出寫到「hotspot.log」文件中。除了編譯方法的不少細節以外,你也能夠看到編譯器線程啓動的任務。注意-XX:+LogCompilation 須要使用-XX:+UnlockExperimentalVMOptions來解鎖。

JVM甚至容許咱們看到從字節碼編譯生成到本地代碼。使用-XX:+PrintOptoAssembly,由編譯器線程生成的本地代碼被輸出並寫到「hotspot.log」文件中。使用這個參數要求運行的服務端VM是debug版本。咱們能夠研究-XX:+PrintOptoAssembly的輸出,以致於瞭解JVM實際執行什麼樣的優化,例如,關於死代碼的消除。一個很是有趣的文章提供了一個例子

關於XX參數的更多信息

若是這篇文章勾起了你的興趣,你能夠本身看一下HotSpot JVM的XX 參數。這裏是一個很好的起點

原創文章,轉載請註明: 轉載自併發編程網 – ifeve.com本文連接地址: JVM實用參數(二)參數分類和即時(JIT)編譯器診斷

相關文章
相關標籤/搜索