全功能內置分析器,如 JConsole 和 VisualVM 的成本有時比它們的性能費用還要高 — 尤爲是在生產軟件上運行的系統中。所以,在聚焦 Java 性能監控的第 2 篇文章中,我將介紹 5 個命令行分析工具,使開發人員僅關注運行的 Java 進程的一個方面。編程
JDK 包括不少命令行實用程序,能夠用於監控和管理 Java 應用程序性能。雖然大多數這類應用程序都被標註爲 「實驗型」,在技術上不受支持,可是它們頗有用。有些甚至是特定用途工具的種子材料,可使用 JVMTI 或 JDI(參見 參考資料)創建。瀏覽器
不少命令行工具都要求您識別您但願監控的 Java 進程。這與監控本地操做系統進程、一樣須要一個程序識別器的同類工具沒有太大區別。jsp
「VMID」 識別器與本地操做系統進程識別器(「pid」)並不老是相同的,這就是咱們須要 JDK jps
實用程序的緣由。ide
jps
— 名稱反映了在大多數 UNIX 系統上發現的 ps
實用程序 — 告訴咱們運行 Java 應用程序的 JVMID。顧名思義,jps
返回指定機器上運行的全部已發現的 Java 進程的 VMID。若是 jps
沒有發現進程,並不意味着沒法附加或研究 Java 進程,而只是意味着它並未宣傳本身的可用性。
若是發現 Java 進程,jps
將列出啓用它的命令行。這種區分 Java 進程的方法很是重要,由於只要涉及操做系統,全部的 Java 進程都被統稱爲 「java
」。在大多數狀況下,VMID 是值得注意的重要數字。
使用分析實用程序開始的最簡單方法是使用一個如在demo/jfc/SwingSet2
中發現的 SwingSet2 演示同樣的演示程序。這樣就能夠避免程序做爲背景/監控程序運行時出現掛起的可能性。當您瞭解工具及其費用後,就能夠在實際程序中進行試用。
加載演示應用程序後,運行 jps
並注意返回的 vmid
。爲了得到更好的效果,採用 -Dcom.sun.management.jmxremote
屬性集啓動 Java 進程。若是沒有使用該設置,部分下列工具收集的部分數據可能不可用。
jstat
實用程序能夠用於收集各類各樣不一樣的統計數據。jstat
統計數據被分類到 「選項」 中,這些選項在命令行中被指定做爲第一參數。對於 JDK 1.6 來講,您能夠經過採用命令 -options
運行 jstat
查看可用的選項清單。清單 1 中顯示了部分選項:
清單 1. jstat 選項
-class -compiler -gc -gccapacity -gccause -gcnew -gcnewcapacity -gcold -gcoldcapacity -gcpermcapacity -gcutil -printcompilation |
實用程序的 JDK 記錄(參見 參考資料)將告訴您清單 1 中每一個選項返回的內容,可是其中大多數用於收集垃圾的收集器或者其部件的性能信息。-class
選項顯示了加載及未加載的類(使其成爲檢測應用程序服務器或代碼中 ClassLoader
泄露的重要實用程序,且 -compiler
和 -printcompilation
都顯示了有關 Hotspot JIT 編譯程序的信息。
默認狀況下,jstat
在您覈對信息時顯示信息。若是您但願每隔必定時間拍攝快照,請在 -options
指令後以毫秒爲單位指定間隔時間。jstat
將持續顯示監控進程信息的快照。若是您但願 jstat
在終止前進行特定數量的快照,在間隔時間/時間值後指定該數字。
若是 5756 是幾分鐘前開始的運行 SwingSet2 程序的 VMID,那麼下列命令將告訴 jstat
每 250 毫秒爲 10 個佚代執行一次 gc 快照轉儲,而後中止:
jstat -gc 5756 250 10 |
請注意 Sun(如今的 Oracle)保留了在不進行任何預先通知的狀況下更改各類選項的輸出甚至是選項自己的權利。這是使用不受支持實用程序的缺點。請參看 Javadocs 瞭解 jstat
輸出中每一列的所有細節。
瞭解 Java 進程及其對應的執行線程內部發生的狀況是一種常見的診斷挑戰。例如,當一個應用程序忽然中止進程時,很明顯出現了資源耗盡,可是僅經過查看代碼沒法明確知道何處出現資源耗盡,且爲何會發生。
jstack
是一個能夠返回在應用程序上運行的各類各樣線程的一個完整轉儲的實用程序,您可使用它查明問題。
採用指望進程的 VMID 運行 jstack
會產生一個堆轉儲。就這一點而言,jstack
與在控制檯窗口內按 Ctrl-Break 鍵起一樣的做用,在控制檯窗口中,Java 進程正在運行或調用 VM 內每一個 Thread
對象上的 Thread.getAllStackTraces()
或Thread.dumpStack()
。jstack
調用也轉儲關於在 VM 內運行的非 Java 線程的信息,這些線程做爲 Thread
對象並不老是可用的。
jstack
的 -l
參數提供了一個較長的轉儲,包括關於每一個 Java 線程持有鎖的更多詳細信息,所以發現(和 squash)死鎖或可伸縮性 bug 是極其重要的。
有時,您正在處理的問題是一個對象泄露,如一個 ArrayList
(可能持有成千上萬個對象)該釋放時沒有釋放。另外一個更廣泛的問題是,看似從不會壓縮的擴展堆,卻有活躍的垃圾收集。
當您努力尋找一個對象泄露時,在指定時刻對堆及時進行拍照,而後審查其中內容很是有用。jmap
經過對堆拍攝快照來提供該功能的第一部分。而後您能夠採用下一部分中描述的 jhat
實用程序分析堆數據。
與這裏描述的其餘全部實用程序同樣,使用 jmap
很是簡單。將 jmap
指向您但願拍快照的 Java 進程的 VMID,而後給予它部分參數,用來描述產生的結果文件。您要傳遞給 jmap
的選項包括轉儲文件的名稱以及是否使用一個文本文件或二進制文件。二進制文件是最有用的選項,可是隻有當與某一種索引工具 結合使用時 — 經過十六進制值的文本手動操做數百兆字節不是最好的方法。
隨意看一下 Java 堆的更多信息,jmap
一樣支持 -histo
選項。-histo
產生一個對象文本柱狀圖,如今在堆中大量引用,由特定類型消耗的字節總數分類。它一樣給出了特定類型的總示例數量,支持部分原始計算,並猜想每一個實例的相對成本。
不幸的是,jmap
沒有像 jstat
同樣的 period-and-max-count 選項,可是將 jmap
(或 jmap.main()
)調用放入 shell 腳本或其餘類的循環,週期性地拍攝快照相對簡單。(事實上,這是加入 jmap
的一個好的擴展,不論是做爲 OpenJDK 自己的源補丁,仍是做爲其餘實用程序的擴展。)
5. jhat (com.sun.tools.hat.Main)
將堆轉儲至一個二進制文件後,您就可使用 jhat
分析二進制堆轉儲文件。jhat
建立一個 HTTP/HTML 服務器,該服務器能夠在瀏覽器中被瀏覽,提供一個關於堆的 object-by-object 視圖,及時凍結。根據對象引用草率處理堆可能會很是好笑,您能夠經過對整體混亂進行某種自動分析而得到更好的服務。幸運的是,jhat
支持 OQL 語法進行這樣的分析。
例如,對全部含有超過 100 個字符的 String
運行 OQL 查詢看起來以下:
select s from java.lang.String s where s.count >= 100 |
結果做爲對象連接顯示,而後展現該對象的完整內容,字段引用做爲能夠解除引用的其餘連接的其餘對象。OQL 查詢一樣能夠調用對象的方法,將正則表達式做爲查詢的一部分,並使用內置查詢工具。一種查詢工具,referrers()
函數,顯示了引用指定類型對象的全部引用。下面是尋找全部參考 File
對象的查詢:
select referrers(f) from java.io.File f |
您能夠查找 OQL 的完整語法及其在 jhat
瀏覽器環境內 「OQL Help」 頁面上的特性。將 jhat
與 OQL 相結合是對行爲不當的堆進行對象調查的有效方法。
當您須要近距離觀察 Java 進程內發生的事情時,JDK 的分析擴展會很是有用。本文中介紹的全部工具均可以從命令行中由其本身使用。它們還能夠與 JConsole 或 VisualVM 有力地結合使用。JConsole 和 VisualVM 提供 Java 虛擬機的整體視圖,jstat
和 jmap
等有針對性的工具支持您對研究進行微調。
走進 5 件事 系列: 編寫腳本。