你所不知道的五件事情--JVM的命令行選項
這是Ted Neward在IBM developerWorks中5 things系列文章中的一篇,講述了關(guān)于JVM命令行參數(shù)的一些應(yīng)用竅門,值得大家學習。(2010.09.01最后更新)摘要:Java虛擬機有數(shù)百個命令行選項,只有經(jīng)驗十分豐富的Java開發(fā)員才會使用這些選項去調(diào)優(yōu)Java運行時環(huán)境。學習如何監(jiān)控并記錄編譯器性能,禁用顯示的垃圾收集(System.gc()),擴展JRE,及其它。
JVM是Java應(yīng)用程序功能與性能背后的實際工作者,大部分Java開發(fā)者認為這是理所當然的。然而我們中很少有人真正地理解JVM是如何做到這些事的--如,分配并收集垃圾對象,擺弄線程,打開及關(guān)閉文件,解釋和/或使用JIT編譯Java字節(jié)碼,以及其它任務(wù)。
不熟悉JVM不僅會使你在應(yīng)用程序的性能方面付出代價,而且當JVM出了某些問題時,還很難進行修復(fù)。
5 things系列的這篇文章介紹一些好用的命令行JVM選項,你可以使用它們?nèi)ピ\斷并調(diào)優(yōu)Java虛擬機的性能。
1. DisableExplicitGC
我無法告訴你我已經(jīng)多少次被要求就應(yīng)用程序性能問題進行咨詢了,而我只是簡單地對代碼文件執(zhí)行g(shù)rep命令就能發(fā)現(xiàn)如清單1所示的程序--這就是早期Java性能的反模式:
Listing 1. System.gc();
// We just released a bunch of objects, so tell the stupid
// garbage collector to collect them already!
System.gc();
// garbage collector to collect them already!
System.gc();
顯式地執(zhí)行垃圾收集器確實是個壞主意--這就像把你自己與一個瘋狂的斗牛犬鎖在電話亭中那樣。雖然該調(diào)用的確切語義依賴于具體的實現(xiàn),但假設(shè)你的JVM運行著一個分代垃圾收集器(大多數(shù)JVM也正是如此),System.gc()方法強制要求VM對Heap進行"全面清掃",即便有些并不是必要的。一般地,全面清掃的開銷要比常規(guī)GC操作要大幾個數(shù)量級,而常規(guī)GC操作的開銷只會產(chǎn)生純數(shù)學的負作用。
但不要相信我的話--為了這個特殊的人為錯誤,Sun的工程師們?yōu)槲覀兲峁┝艘粋€JVM選項:-XX:+DisableExplicitGC選項自動地將System.gc()調(diào)用置為無用操作,給你一個機會去執(zhí)行程序,并讓你自己看看System.gc()是否已經(jīng)幫助或損害整個JVM的執(zhí)行。
2. HeapDumpOnOutOfMemoryError
你肯定曾經(jīng)遇到過JVM不斷死掉的情況,即拋出OutOfMemoryError,在你的一生中似乎都不能設(shè)置一個調(diào)試器去捕獲這個錯誤,并看看是出了什么問題?像這些不時發(fā)生且/或無法確定的問題能使一個開發(fā)員完全瘋掉。
你所想的,就是在在JVM快死掉時能夠捕獲Heap的快照--這正是命令-XX:+HeapDumpOnOutOfMemoryError所要做的。
運行該命令就是告訴JVM創(chuàng)建一個"Heap轉(zhuǎn)儲快照",并將它保存為一個文件以便于處理,常常使用jhat工具(在前一篇文章中我有介紹過)。使用相應(yīng)的命令-XX:HeapDumpPath,就指定被保存文件的確切路徑。(不論文件保存在何處,要先確保文件系統(tǒng)和/或Java進程有必要的僅限配置能在那兒寫文件。)
3. bootclasspath
偶爾地,將某個類,該類不同于原有的或以某種方式擴展而得到的JRE所存儲的類,置于類路徑中是有用的。(一個例子就是新的Java Crypto API適配器)。如果你想擴展JRE,那么你的定制實現(xiàn)需要能用于引導(dǎo)ClassLoader,該ClassLoader會加載java.lang.Object以及它在rt.jar中的所有同族成員。
盡管你可以打開rt.jar并將你定制實現(xiàn)或新的包放入其中,但在技術(shù)上這就違反了當初你在下載JDK時所同意的協(xié)議。
相反地,應(yīng)該使用JVM自己的-Xbootclasspath選項,以及它的兄弟選項-Xbootclasspath/p和-Xbootclasspath/a。
-Xbootclasspath允許你設(shè)置整個引導(dǎo)類路徑,該路徑一般地必須包含一個針對rt.jar的引用,再加上一批隨JDK發(fā)布但不是rt.jar一部分的其它JAR文件。-Xbootclasspath/p會向已有引導(dǎo)類路徑中預(yù)置值,然后-Xbootclasspath/a會將它連接起來。
例如,如果你已經(jīng)修改了原有的java.lang.Integer,并將修改后的程序放入子目錄,mods,然后參數(shù)-Xbootclasspath/a mods將會在加載原有默認Integer類之前加載這個新的Integer實現(xiàn)。
4. verbose
對于任何類型的Java應(yīng)用,-verbose都是頭等有用的診斷工具。該選項有三個子選項:gc,class和jni。
一般地,如果JVM垃圾收集器在運行并導(dǎo)致了低下的性能,那么gc就是開發(fā)員們第一次想要弄清楚的地方。不幸地是,解釋gc的輸出需要些技巧,這足夠成為一整本書的主題了。更糟地是,在命令行窗口打印出的輸出對于不同的Java發(fā)行版是不同的,或者對于不同的JVM也是不同的,這造成難以對其進行正確地解釋。
一般來說,如果垃圾收集器是分代收集器(大多數(shù)"企業(yè)級"VM正是如此),會有一些布爾型選項去指定是否使用完全清除;在Sun的JVM中,這樣的選項會以[Full GC ...]的形式出現(xiàn)在GC輸出行的開頭。
class會是一個生命保護者,它嘗試著診斷ClassLoader和/或不匹配的類沖突。它不僅會報告類是在何時被加載的,而且也會報告類是從何處被加載的,包括JAR文件的路徑,如果這個類來自于一個JAR的話。
jni很少用到,除非你有使用JNI和原生庫。當啟用該選項時,它會報告各種JNI事件,例如原生庫何時被加載的,以及方法何時被綁定的;另外,該報告輸出會由于不同的Java版或JVM實現(xiàn)而不盡相同。
5. 命令行-X
我已經(jīng)列出了一些JVM提供的且我比較喜歡的命令行選項,但還有更多的選項你自己就能發(fā)現(xiàn)到。運行命令行參數(shù)-X列出所有JVM支持的非標準的(但多數(shù)就是安全的)參數(shù)--就像這些:
-Xint,使JVM以解釋模式進行運行(對于測試JIT編譯器是否真的作用到你的程序,或是證明你是否有Bug在JIT編譯器中,該選項是很用的)。
-Xloggc:,該參數(shù)所做的與-verbose:gc相同,但它會把日志記錄到一個文件中,而不是一股腦地輸出在命令窗口中。
JVM命令行選項改了一次又一次,所以隔一段時間再去看會是一個好主意。這種區(qū)別就好像是,花上一整夜金城湯池盯著你的顯示器,或者是下午五點就回家與愛人和孩子享用美餐(或是在Mass Effect 2中屠殺敵人,這取決于你的偏好)。
結(jié)論
命令行選項的本意不是為了在產(chǎn)品環(huán)境中永久使用--事實上,除了你(可能)最終用于調(diào)優(yōu)JVM垃圾收集器的選項之外,非標準的命令選項都不會被刻意地用到產(chǎn)品中。但作為能夠窺探到完全不透明的虛擬機的內(nèi)部工部情形的工具,它們是無價的。
5 things系列的下一篇文章:Java日常工具。