JDK命令行工具
在JDK的開發(fā)包中,除了大家熟知的JAVA.exe和javac.exe外,還有一系列輔助工具。這些輔助工具位于JDK安裝目錄下的bin目錄中,可以幫助開發(fā)人員很好地解決Java應(yīng)用程序的一些“疑難雜癥”。圖6.16顯示了部分輔助工具。
乍看之下,雖然這些工具都是.exe的可執(zhí)行文件,但事實(shí)上它們只是Java程序的一層包裝,其真正的實(shí)現(xiàn)是在tools.jar中,如圖6.17所示。
以jps工具為例,在控制臺執(zhí)行jps命令和
java-classpath%Java_HOME%/lib/tools.jarsun.tools.jps.Jps命令是等價(jià)的,即jps.exe只是這個(gè)命令的一層包裝。
jps命令
jps命令類似于linux下的ps,但它只用于列出Java的進(jìn)程。直接運(yùn)行jps命令不加任何參數(shù),可以列出Java程序進(jìn)程ID及Main函數(shù)短名稱,例如:
從這個(gè)輸出結(jié)果中可以看到,當(dāng)前系統(tǒng)中共存在3個(gè)Java應(yīng)用程序,其中第一個(gè)輸出Jps就是jps命令本身,這也證明了此命令的本質(zhì)是一個(gè)Java程序。此外,jps還提供了一系列參數(shù)來控制它的輸出內(nèi)容。
參數(shù)-q可以指定jps只輸出進(jìn)程ID,而不輸出類的短名稱,例如:
參數(shù)-m可以用于輸出傳遞給Java進(jìn)程(主函數(shù))的參數(shù),例如:
參數(shù)-l可以用于輸出主函數(shù)的完整路徑,例如:
參數(shù)-v可以顯示傳遞給JVM的參數(shù),例如:
注意:jps命令類似于ps命令,但是它只列出系統(tǒng)中所有的Java應(yīng)用程序。通過jps命令可以方便地查看Java進(jìn)程的啟動類、傳入?yún)?shù)和JVM參數(shù)等信息。
jstat命令
jstat是一個(gè)可用于觀察Java應(yīng)用程序運(yùn)行信息的工具。它的功能非常強(qiáng)大,可以通過它查看堆信息的詳細(xì)情況。其基本使用語法如下:
其中,選項(xiàng)option可以由以下值構(gòu)成。
·-class:顯示ClassLoader的相關(guān)信息。
·-compiler:顯示JIT編譯的相關(guān)信息。
·-gc:顯示與GC操作相關(guān)的堆信息。
·-gccapacity:顯示各個(gè)代的容量及使用情況。
·-gccause:顯示垃圾收集的相關(guān)信息(同-gcutil),同時(shí)顯示最后一次或當(dāng)前正在發(fā)生的垃圾收集的誘發(fā)原因。
·-gcnew:顯示新生代信息。
·-gcnewcapacity:顯示新生代的大小與使用情況。
·-gcold:顯示老年代和永久代的信息。
·-gcoldcapacity:顯示老年代的大小。
·-gcpermcapacity:顯示永久代的大小。
·-gcutil:顯示垃圾收集信息。
·-printcompilation:輸出JIT編譯的方法信息。相關(guān)參數(shù)含義如下:
·-t:可以在輸出信息前加上一個(gè)Timestamp列,顯示程序的運(yùn)行時(shí)間。
·-h:可以設(shè)定在周期性數(shù)據(jù)輸出時(shí),當(dāng)輸出多少行數(shù)據(jù)后跟著輸出一個(gè)表頭信息。
·interval:用于指定輸出統(tǒng)計(jì)數(shù)據(jù)的周期,單位為ms。
·count:用于指定一共輸出多少次數(shù)據(jù)。
如下示例輸出Java進(jìn)程2972的ClassLoader相關(guān)信息。每秒統(tǒng)計(jì)一次信息,一共輸出2次。
在以上的輸出結(jié)果中,Loaded表示載入類的數(shù)量,第1個(gè)Bytes表示載入類的合計(jì)大小,Unloaded表示卸載類的數(shù)量,第2個(gè)Bytes表示卸載類的大小,Time表示在加載類和卸載類上所花的時(shí)間。
下例顯示了查看JIT編譯的信息。
其中,Compiled表示編譯任務(wù)執(zhí)行的次數(shù),F(xiàn)ailed表示編譯失敗的次數(shù),Invalid表示編譯不可用的次數(shù),Time表示編譯的總耗時(shí),F(xiàn)ailedType表示最后一次編譯失敗的類型,F(xiàn)ailedMethod表示最后一次編譯失敗的類名和方法名。
下例顯示了與GC操作相關(guān)的堆信息輸出結(jié)果。
其中,各列的信息含義如下:
·S0C:s0(from)的大小(KB)。
·S1C:s1(from)的大小(KB)。
·S0U:s0(from)已使用的空間(KB)。
·S1U:s1(from)已使用的空間(KB)。
·EC:eden區(qū)的大小(KB)。
·EU:eden區(qū)已使用的空間(KB)。
·OC:老年代的大小(KB)。
·OU:老年代已經(jīng)使用的空間(KB)。
·PC:永久區(qū)的大小(KB)。
·PU:永久區(qū)已使用的空間(KB)。
·YGC:新生代GC操作次數(shù)。
·YGCT:新生代GC操作耗時(shí)。
·FGC:FullGC操作次數(shù)。
·FGCT:FullGC操作耗時(shí)。
·GCT:GC操作總耗時(shí)。
下例顯示了各個(gè)代的信息,與-gc相比,它不僅輸出了各個(gè)代的當(dāng)前大小,也包含各個(gè)代的最大值和最小值。
其中部分列的信息含義如下:
·NGCMN:新生代最小值(KB)。
·NGCMX:新生代最大值(KB)。
·NGC:當(dāng)前新生代大小(KB)。
·OGCMN:老年代最小值(KB)。
·OGCMX:老年代最大值(KB)。
·PGCMN:永久代最小值(KB)。
·PGCMX:永久代最大值(KB)。
下例顯示了最近一次執(zhí)行GC操作的原因及當(dāng)前執(zhí)行GC操作的原因。
其中部分列的信息含義如下:
·LGCC:上次執(zhí)行GC操作的原因。
·GCC:當(dāng)前執(zhí)行GC操作的原因。
以上輸出結(jié)果顯示,最近一次執(zhí)行GC操作是由于顯式的System.gc()調(diào)用所引起的,當(dāng)前時(shí)刻未執(zhí)行GC操作。
-gcnew參數(shù)可以用于查看新生代的一些詳細(xì)信息。
其中部分列的信息含義如下:
·TT:新生代對象晉升到老年代對象的年齡。
·MTT:新生代對象晉升到老年代對象的年齡最大值。
·DSS:所需的survivor區(qū)大小。
-gcnewcapacity參數(shù)可以詳細(xì)地輸出新生代各個(gè)區(qū)的大小信息。
其中部分列信息如下:
·S0CMX:s0區(qū)的最大值(KB)。
·S1CMX:s1區(qū)的最大值(KB)。
·ECMX:eden區(qū)的最大值(KB)。
-gcold參數(shù)用于展現(xiàn)老年代GC操作的概況。
-gcoldcapacity參數(shù)用于展現(xiàn)老年代的容量信息。
-gcpermcapacity參數(shù)用于展示永久區(qū)的使用情況。
-gcutil參數(shù)用于展示GC回收的相關(guān)信息。
其中部分列的信息如下:
·S0:s0區(qū)使用的百分比。
·S1:s1區(qū)使用的百分比。
·E:eden區(qū)使用的百分比。
·O:old區(qū)使用的百分比。
·P:永久區(qū)使用的百分比。
注意:jstat命令可以非常詳細(xì)地查看Java應(yīng)用程序的堆使用情況及GC操作情況。
jinfo命令
jinfo可以用來查看正在運(yùn)行的Java應(yīng)用程序的擴(kuò)展參數(shù),甚至支持在運(yùn)行時(shí)修改部分參數(shù)。其基本語法如下:
其中,option可以為以下信息:
·-flag<name>:打印指定JVM的參數(shù)值。
·-flag[+|-]<name>:設(shè)置指定JVM參數(shù)的布爾值。
·-flag<name>=<value>:設(shè)置指定JVM參數(shù)的值。
在很多情況下,Java應(yīng)用程序不會指定所有的JVM參數(shù),此時(shí),開發(fā)人員可能不知道某一個(gè)具體的JVM參數(shù)的默認(rèn)值。在這種情況下,需要通過查找文檔獲取某個(gè)參數(shù)的默認(rèn)值。這個(gè)查找過程可能是非常艱難的,但有了jinfo工具,開發(fā)人員可以很方便地找到JVM參數(shù)的當(dāng)前值。
例如,下例顯示了新生代對象晉升到老年代對象的最大年齡。在應(yīng)用程序啟動時(shí)并沒有指定這個(gè)參數(shù),但通過jinfo可以查看這個(gè)參數(shù)的當(dāng)前數(shù)值。
下例顯示了是否打印GC的詳細(xì)信息。
除了查找參數(shù)的值,jinfo也支持修改部分參數(shù)的數(shù)值,當(dāng)然這個(gè)修改能力是極其有限的。下例中通過調(diào)用jinfo對PrintGCDetails參數(shù)進(jìn)行修改,jinfo可以在Java程序運(yùn)行時(shí)關(guān)閉或者打開這個(gè)開關(guān)。
注意:jinfo不僅可以查看運(yùn)行時(shí)某一個(gè)JVM參數(shù)的實(shí)際取值,甚至可以在運(yùn)行時(shí)修改部分參數(shù),并使之立即生效。
jmap命令
jmap命令可以生成Java應(yīng)用程序的堆快照和對象的統(tǒng)計(jì)信息。
下例使用jmap命令生成PID為2972的Java程序的對象統(tǒng)計(jì)信息,并輸出到s.txt文件中。
可以看到,這個(gè)輸出結(jié)果顯示了內(nèi)存中的實(shí)例數(shù)量和合計(jì)。
jmap命令的另一個(gè)更為重要的功能是得到Java程序的當(dāng)前堆快照,例如執(zhí)行以下命令:
本例中,將應(yīng)用程序的堆快照輸出到C盤的heap.bin文件中。之后,便可以通過多種工具分析該文件,例如6.3.5節(jié)中將要介紹的jhat工具。這里使用VisualVM工具打開這個(gè)快照文件,如圖6.18所示。
注意:jmap命令可用于導(dǎo)出Java應(yīng)用程序的堆快照。
jhat命令
使用jhat命令可以分析Java應(yīng)用程序的堆快照內(nèi)容。這里分析6.3.4節(jié)中使用jmap命令生成的堆文件heap.hprof,如下:
jhat在分析完成后,使用HTTP服務(wù)器展示其分析結(jié)果。在瀏覽器中訪問http://127.0.0.1:7000,結(jié)果如圖6.19所示。
在默認(rèn)頁中,jhat服務(wù)器顯示了所有的非平臺類信息。單擊鏈接進(jìn)入,可以查看選中類的超類、ClassLoader及該類的實(shí)例等信息。此外,在頁面的底部,jhat還為開發(fā)人員提供了其他查詢方式,如圖6.20所示。
通過這些鏈接,開發(fā)者可以進(jìn)一步查看所有類的信息(包括Java平臺的類)、所有類的實(shí)例數(shù)量及實(shí)例的具體信息。最后,還有一個(gè)鏈接指向OQL查詢界面。
圖6.21顯示了在jhat中查看Java應(yīng)用程序中java.lang.String類的實(shí)例數(shù)量。
單擊instances鏈接可以進(jìn)一步查看String對象的實(shí)例,如圖6.22所示。
通常,導(dǎo)出的堆快照信息非常多,因此可能很難通過頁面上簡單的鏈接索引找到想要的信息。為此,jhat還支持使用OQL語句對堆快照進(jìn)行查詢。執(zhí)行OQL語句的界面非常簡潔,如圖6.23所示。例如,使用OQL查詢出當(dāng)前Java程序中所有java.io.File對象的路徑,則OQL語句如下:
jhat的OQL語句與VisualVM的OQL非常接近,有興趣的讀者可以查閱本書中的相關(guān)章節(jié)。
注意:jhat命令可以對堆快照文件進(jìn)行分析,它啟動一個(gè)HTTP服務(wù)器,開發(fā)人員可以通過瀏覽器瀏覽Java堆快照。
jstack命令
jstack命令可用于導(dǎo)出Java應(yīng)用程序的線程堆棧。其語法如下:
其中,-l選項(xiàng)用于打印鎖的附加信息。
jstack工具會在控制臺輸出程序中所有的鎖信息,并可以使用重定向?qū)⑤敵鼋Y(jié)果保存到文件中。例如:
下例演示了一個(gè)簡單的死鎖,兩個(gè)線程分別占用south鎖和north鎖,并同時(shí)請求對方占用的鎖,導(dǎo)致死鎖發(fā)生。
使用jstack工具打印上例的輸出結(jié)果,部分結(jié)果如下:
從jstack工具的輸出結(jié)果中可以很容易地找到發(fā)生死鎖的兩個(gè)線程及死鎖線程的持有對象和等待對象,從而幫助開發(fā)人員解決死鎖問題。
注意:通過jstack工具不僅可以得到線程堆棧,還可以自動進(jìn)行死鎖檢查,并輸出找到的死鎖信息。
jstatd命令
在本節(jié)之前所述的工具中,只涉及監(jiān)控本機(jī)的Java應(yīng)用程序,而在這些工具中,一些監(jiān)控工具也支持對遠(yuǎn)程計(jì)算機(jī)的監(jiān)控(如jps、jstat)。為了啟用遠(yuǎn)程監(jiān)控,則需要配合使用jstatd命令。
jstatd命令是一個(gè)RMI服務(wù)端程序,它的作用相當(dāng)于代理服務(wù)器,建立本地計(jì)算機(jī)與遠(yuǎn)程監(jiān)控工具的通信。jstatd服務(wù)器將本地的Java應(yīng)用程序信息傳遞到遠(yuǎn)程計(jì)算機(jī)上,如圖6.24所示。
直接打開jstatd服務(wù)器可能會拋出拒絕訪問的異常:
這是由于jstatd程序沒有足夠的權(quán)限所致。可以使用Java的安全策略,為其分配相應(yīng)的權(quán)限。下面的代碼為jstatd分配了最大的權(quán)限,并將其保存在jstatd.all.policy文件中。
使用以下命令再次開啟jstatd服務(wù)器:
這樣,服務(wù)器開啟成功。
注意:-J參數(shù)是一個(gè)公共參數(shù),如jps和jstat等命令都可以接受這個(gè)參數(shù)。由于jps和jstat命令本身也是Java應(yīng)用程序,-J參數(shù)可以為jps等命令本身設(shè)置其JVM參數(shù)。
默認(rèn)情況下,jstatd將在1099端口開啟RMI服務(wù)器。
以上命令行顯示,本機(jī)的1099端口處于監(jiān)聽狀態(tài),相關(guān)進(jìn)程號是3656。使用jsp命令查看3656進(jìn)程正是jstatd,說明jstatd啟動成功。
下面使用jps顯示遠(yuǎn)程計(jì)算機(jī)的Java進(jìn)程,執(zhí)行命令如下:
使用jstat顯示遠(yuǎn)程進(jìn)程460的GC操作情況,執(zhí)行命令如下:
hprof工具
與前文中介紹的監(jiān)控工具不同,hprof不是獨(dú)立的監(jiān)控工具,它只是一個(gè)Javaagent工具,可以用于監(jiān)控Java應(yīng)用程序在運(yùn)行時(shí)的CPU信息和堆信息。使用java-agentlib:hprof=help命令可以查看hprof的幫助文檔。下面是hprof工具幫助信息的輸出結(jié)果,加粗部分是常用的參數(shù)。
使用hprof工具可以查看程序中各個(gè)函數(shù)的CPU占用時(shí)間。以下代碼包含3個(gè)方法,分別占用不同的CPU時(shí)間。
使用參數(shù)-agentlib:hprof=cpu=times,interval=10運(yùn)行以上代碼。times選項(xiàng)將會在Java函數(shù)的調(diào)用前后記錄函數(shù)的執(zhí)行時(shí)間,進(jìn)而計(jì)算函數(shù)的執(zhí)行時(shí)間。程序運(yùn)行的部分輸出結(jié)果如下:
可以很容易地看到運(yùn)行時(shí)間最長的函數(shù)。
使用參數(shù)-agentlib:hprof=heap=dump,format=b,file=c:core.hprof運(yùn)行程序,可以將應(yīng)用程序的堆快照保存在指定文件c:core.hprof中。使用MAT或者VisualVM等工具可以分析這個(gè)堆文件。
使用參數(shù)-agentlib:hprof=heap=sites運(yùn)行程序,可以輸出Java應(yīng)用程序中各個(gè)類所占的內(nèi)存百分比。部分輸出結(jié)果如下:
注意:使用hprof工具可以監(jiān)控Java應(yīng)用程序各個(gè)函數(shù)的運(yùn)行時(shí)間,或?qū)С龀绦虻亩芽煺铡?/p>
jcmd命令
在JDK7之后,JDK新增了一個(gè)命令行工具jcmd。不同于之前的命令行工具,jcmd是一個(gè)多功能工具,它幾乎包括之前介紹的所有命令的功能。
如下命令類似于jps,列出當(dāng)前正在運(yùn)行的Java程序。
下面的命令顯示了給定程序的啟動時(shí)間。
不同于之前的命令行工具,jcmd的使用更加友好,它不僅支持pid作為輸入,也可以使用主程序類名作為其輸入,例如:
它足夠“聰明”,甚至可以只寫類的短名稱,例如:
還可以通過jcmd很輕松地打印線程堆棧(類似于jstack),例如:
也可以通過jcmd打印類的柱狀圖信息(類似于jmap-histo),例如:
同樣支持dump整個(gè)堆數(shù)據(jù),例如:
上述命令會將整個(gè)堆信息轉(zhuǎn)存到D:d.dump文件中。
還可以查看進(jìn)程啟動時(shí)的虛擬機(jī)參數(shù),例如: