全功能內(nèi)置分析器,如 JConsole 和 VisualVM 的成本有時比它們的性能費用還要高 —尤其是在生產(chǎn)軟件上運行的系統(tǒng)中。因此,在聚焦 Java 性能監(jiān)控的第 2 篇文章中,我將介紹 5 個命令行分析工具,使開發(fā)人員僅關(guān)注運行的Java 進程的一個方面。
JDK 包括很多命令行實用程序,可以用于監(jiān)控和管理 Java 應(yīng)用程序性能。雖然大多數(shù)這類應(yīng)用程序都被標注為 “實驗型”,在技術(shù)上不受支持,但是它們很有用。有些甚至是特定用途工具的種子材料,可以使用 JVMTI 或 JDI(參見 參考資料)建立。
很多命令行工具都要求您識別您希望監(jiān)控的 Java 進程。這與監(jiān)控本地操作系統(tǒng)進程、同樣需要一個程序識別器的同類工具沒有太大區(qū)別。
“VMID” 識別器與本地操作系統(tǒng)進程識別器(“pid”)并不總是相同的,這就是我們需要 JDK jps
實用程序的原因。
jps
— 名稱反映了在大多數(shù) UNIX 系統(tǒng)上發(fā)現(xiàn)的 ps
實用程序 — 告訴我們運行 Java 應(yīng)用程序的 JVMID。顧名思義,jps
返回指定機器上運行的所有已發(fā)現(xiàn)的 Java 進程的 VMID。如果 jps
沒有發(fā)現(xiàn)進程,并不意味著無法附加或研究 Java 進程,而只是意味著它并未宣傳自己的可用性。
如果發(fā)現(xiàn) Java 進程,jps
將列出啟用它的命令行。這種區(qū)分 Java 進程的方法非常重要,因為只要涉及操作系統(tǒng),所有的 Java 進程都被統(tǒng)稱為 “java
”。在大多數(shù)情況下,VMID 是值得注意的重要數(shù)字。
使用分析實用程序開始的最簡單方法是使用一個如在 demo/jfc/SwingSet2
中發(fā)現(xiàn)的 SwingSet2 演示一樣的演示程序。這樣就可以避免程序作為背景/監(jiān)控程序運行時出現(xiàn)掛起的可能性。當(dāng)您了解工具及其費用后,就可以在實際程序中進行試用。
加載演示應(yīng)用程序后,運行 jps
并注意返回的 vmid
。為了獲得更好的效果,采用 -Dcom.sun.management.jmxremote
屬性集啟動 Java 進程。如果沒有使用該設(shè)置,部分下列工具收集的部分數(shù)據(jù)可能不可用。
jstat
實用程序可以用于收集各種各樣不同的統(tǒng)計數(shù)據(jù)。jstat
統(tǒng)計數(shù)據(jù)被分類到 “選項” 中,這些選項在命令行中被指定作為第一參數(shù)。對于 JDK 1.6 來說,您可以通過采用命令 -options
運行 jstat
查看可用的選項清單。清單 1 中顯示了部分選項:
清單 1. jstat 選項
-class -compiler -gc -gccapacity -gccause -gcnew -gcnewcapacity -gcold -gcoldcapacity -gcpermcapacity -gcutil -printcompilation |
實用程序的 JDK 記錄(參見 參考資料)將告訴您清單 1 中每個選項返回的內(nèi)容,但是其中大多數(shù)用于收集垃圾的收集器或者其部件的性能信息。-class
選項顯示了加載及未加載的類(使其成為檢測應(yīng)用程序服務(wù)器或代碼中 ClassLoader
泄露的重要實用程序,且 -compiler
和 -printcompilation
都顯示了有關(guān) Hotspot JIT 編譯程序的信息。
默認情況下,jstat
在您核對信息時顯示信息。如果您希望每隔一定時間拍攝快照,請在 -options
指令后以毫秒為單位指定間隔時間。jstat
將持續(xù)顯示監(jiān)控進程信息的快照。如果您希望 jstat
在終止前進行特定數(shù)量的快照,在間隔時間/時間值后指定該數(shù)字。
如果 5756 是幾分鐘前開始的運行 SwingSet2 程序的 VMID,那么下列命令將告訴 jstat
每 250 毫秒為 10 個佚代執(zhí)行一次 gc 快照轉(zhuǎn)儲,然后停止:
jstat -gc 5756 250 10 |
請注意 Sun(現(xiàn)在的 Oracle)保留了在不進行任何預(yù)先通知的情況下更改各種選項的輸出甚至是選項本身的權(quán)利。這是使用不受支持實用程序的缺點。請參看 Javadocs 了解 jstat
輸出中每一列的全部細節(jié)。
了解 Java 進程及其對應(yīng)的執(zhí)行線程內(nèi)部發(fā)生的情況是一種常見的診斷挑戰(zhàn)。例如,當(dāng)一個應(yīng)用程序突然停止進程時,很明顯出現(xiàn)了資源耗盡,但是僅通過查看代碼無法明確知道何處出現(xiàn)資源耗盡,且為什么會發(fā)生。
jstack
是一個可以返回在應(yīng)用程序上運行的各種各樣線程的一個完整轉(zhuǎn)儲的實用程序,您可以使用它查明問題。
采用期望進程的 VMID 運行 jstack
會產(chǎn)生一個堆轉(zhuǎn)儲。就這一點而言,jstack
與在控制臺窗口內(nèi)按 Ctrl-Break 鍵起同樣的作用,在控制臺窗口中,Java 進程正在運行或調(diào)用 VM 內(nèi)每個 Thread
對象上的 Thread.getAllStackTraces()
或 Thread.dumpStack()
。jstack
調(diào)用也轉(zhuǎn)儲關(guān)于在 VM 內(nèi)運行的非 Java 線程的信息,這些線程作為 Thread
對象并不總是可用的。
jstack
的 -l
參數(shù)提供了一個較長的轉(zhuǎn)儲,包括關(guān)于每個 Java 線程持有鎖的更多詳細信息,因此發(fā)現(xiàn)(和 squash)死鎖或可伸縮性 bug 是極其重要的。
有時,您正在處理的問題是一個對象泄露,如一個 ArrayList
(可能持有成千上萬個對象)該釋放時沒有釋放。另一個更普遍的問題是,看似從不會壓縮的擴展堆,卻有活躍的垃圾收集。
當(dāng)您努力尋找一個對象泄露時,在指定時刻對堆及時進行拍照,然后審查其中內(nèi)容非常有用。jmap
通過對堆拍攝快照來提供該功能的第一部分。然后您可以采用下一部分中描述的 jhat
實用程序分析堆數(shù)據(jù)。
與這里描述的其他所有實用程序一樣,使用 jmap
非常簡單。將 jmap
指向您希望拍快照的 Java 進程的 VMID,然后給予它部分參數(shù),用來描述產(chǎn)生的結(jié)果文件。您要傳遞給 jmap
的選項包括轉(zhuǎn)儲文件的名稱以及是否使用一個文本文件或二進制文件。二進制文件是最有用的選項,但是只有當(dāng)與某一種索引工具 結(jié)合使用時 — 通過十六進制值的文本手動操作數(shù)百兆字節(jié)不是最好的方法。
隨意看一下 Java 堆的更多信息,jmap
同樣支持 -histo
選項。-histo
產(chǎn)生一個對象文本柱狀圖,現(xiàn)在在堆中大量引用,由特定類型消耗的字節(jié)總數(shù)分類。它同樣給出了特定類型的總示例數(shù)量,支持部分原始計算,并猜測每個實例的相對成本。
不幸的是,jmap
沒有像 jstat
一樣的 period-and-max-count 選項,但是將 jmap
(或 jmap.main()
)調(diào)用放入 shell 腳本或其他類的循環(huán),周期性地拍攝快照相對簡單。(事實上,這是加入 jmap
的一個好的擴展,不管是作為 OpenJDK 本身的源補丁,還是作為其他實用程序的擴展。)
5. jhat (com.sun.tools.hat.Main)
將堆轉(zhuǎn)儲至一個二進制文件后,您就可以使用 jhat
分析二進制堆轉(zhuǎn)儲文件。jhat
創(chuàng)建一個 HTTP/HTML 服務(wù)器,該服務(wù)器可以在瀏覽器中被瀏覽,提供一個關(guān)于堆的 object-by-object 視圖,及時凍結(jié)。根據(jù)對象引用草率處理堆可能會非常可笑,您可以通過對總體混亂進行某種自動分析而獲得更好的服務(wù)。幸運的是,jhat
支持 OQL 語法進行這樣的分析。
例如,對所有含有超過 100 個字符的 String
運行 OQL 查詢看起來如下:
select s from java.lang.String s where s.count >= 100 |
結(jié)果作為對象鏈接顯示,然后展示該對象的完整內(nèi)容,字段引用作為可以解除引用的其他鏈接的其他對象。OQL 查詢同樣可以調(diào)用對象的方法,將正則表達式作為查詢的一部分,并使用內(nèi)置查詢工具。一種查詢工具,referrers()
函數(shù),顯示了引用指定類型對象的所有引用。下面是尋找所有參考 File
對象的查詢:
select referrers(f) from java.io.File f |
您可以查找 OQL 的完整語法及其在 jhat
瀏覽器環(huán)境內(nèi) “OQL Help” 頁面上的特性。將 jhat
與 OQL 相結(jié)合是對行為不當(dāng)?shù)亩堰M行對象調(diào)查的有效方法。
當(dāng)您需要近距離觀察 Java 進程內(nèi)發(fā)生的事情時,JDK的分析擴展會非常有用。本文中介紹的所有工具都可以從命令行中由其自己使用。它們還可以與 JConsole 或 VisualVM有力地結(jié)合使用。JConsole 和 VisualVM 提供 Java 虛擬機的總體視圖,jstat
和 jmap
等有針對性的工具支持您對研究進行微調(diào)。
走進 5 件事 系列: 編寫腳本。
學(xué)習(xí)
- 您知道的 5 件事......(Ted Neward,developerWorks,2010 年):該系列旨在將 Java 技術(shù)瑣事轉(zhuǎn)化為有用的編程技巧,從中找到您對 Java 平臺不了解的事情。
- JDK 工具和實用程序:了解本文中討論的實驗性監(jiān)控及故障發(fā)現(xiàn)和排除工具:
jps
、jstat
、jstack
、jmap
和jhat
。 - “10 大 Java 性能故障發(fā)現(xiàn)及排除工具” (Sajan Kumar,Javalobby,2008 年 7 月):JConsole 是該清單中的首要工具,還有本文為讀者提供的很多其他新工具。
- “獲取 JVM 運行時信息”(Dustin Marx,Dustin's Software Development Cogitations and Speculations,2009 年 6 月):結(jié)合
jps
、jinfo
和JConsole
等 JDK 內(nèi)置監(jiān)控和管理工具的更多方法。 - “ Java 理論和實踐:城市性能傳奇”(Brian Goetz,developerWorks,2003 年 4 月):Brian 對 Java 性能的三個著名 “事實” 進行的調(diào)查。
- “Java 理論與實踐: 用弱引用堵住內(nèi)存泄漏”(Brian Goetz,developerWorks,2005 年 11 月):如何檢測和解決無意識對象保留的常見起因。
- JVMTI(JVM 工具接口):了解更多關(guān)于支持分析、調(diào)試、監(jiān)控、線程分析和覆蓋分析工具的 Java 平臺本地編程接口的信息。JDI(Java 調(diào)試接口) 提供需要遠程訪問 VM 運行狀態(tài)的調(diào)試器的信息。
- developerWorks Java 技術(shù)專區(qū):關(guān)于 Java 編程各個方面的數(shù)百篇文章。