JConsole的使用手冊
一篇Sun項目主頁上介紹JConsole使用的文章,前段時間性能測試的時候大概翻譯了一下以便學習,今天整理一下發上來,有些地方也不知道怎么翻,就保留了原文,可能還好理解點,呵呵,水平有限,翻的不好,大家多多包涵。
JConsole畢竟是JDK自帶的東西,功能雖然沒有一些商業軟件那么強大,但是穩定性好,在大壓力情況下也不會發生什么問題。而且,提供了相對全面的系統監控功能,還是值得一用的。
JConsole
JConsole是一個基于JMX的GUI工具,用于連接正在運行的JVM,不過此JVM需要使用可管理的模式啟動。如果要把一個應用以可管理的形式啟動,可以在啟動是設置com.sun.management.jmxremote
。例如,啟動一個可以在本地監控的J2SE的應用Java2Demo ,需輸入以下命令:
JDK_HOME/bin/java -Dcom.sun.management.jmxremote -jar JDK_HOME/demo/jfc/Java2D/Java2Demo.jar JDK_HOME需要
是一個含有JDK5.0的目錄。
要啟動JConsole,運行 JDK_HOME/bin/jconsole 一個用于連接的對話框將會打開。 對話框的Local標簽列出了所有本地正在運行的JVM,還包含進程的ID等信息。
Figure 2: Local Tab.
JConsole可以以三種方式連接正在運行的JVM:
- Local:使用JConsole連接一個正在本地系統運行的JVM,并且執行程序的和運行JConsole的需要是同一個用戶。JConsole使用文件系統的授權通過RMI連接器連接到平臺的MBean服務器上。這種從本地連接的監控能力只有Sun的JDK具有
- Remote:使用下面的URL通過RMI連接器連接到一個JMX代理:
service:jmx:rmi:///jndi/rmi://hostName:portNum/jmxrmi
hostName填入主機名稱, portNum為JMX代理啟動時指定的端口。JConsole為建立連接,需要在環境變量中設置
mx.remote.credentials來指定用戶名和密碼從而進行授權。
- Advanced:使用一個特殊的URL連接JMX代理。一般情況使用自己定制的連接器而不是RMI提供的連接器來連接JMX代理,或者是一個使用JDK1.4的實現了JMX和JMX Rmote的應用。
當JConsole成功建立連接,它從連接上的JMX代理處獲取信息,并且以下面幾個標簽頁呈現信息。
- Summary tab. 監控JVM和一些監控變量的信息。
- Memory tab. 內存使用信息
- Threads tab. 線程使用信息
- Classes tab. 類調用信息
- VM tab. JVM的信息
- MBeans tab.所有MBeans的信息
MBeans tab展示了所有以一般形式注冊到JVM上的MBeans。MBeans tab允許你獲取所有的平臺信息,包括那些不能從其他標簽頁獲取到的信息。注意,其他標簽頁上的一些信息也在MBeans這里顯示。另外,你可以使用 MBeans標簽管理你自己的應用的MBeans
使用MBeans Tab監控和管理MBean
注冊到JMX代理的平臺或者應用的MBeans,可以通過MBeans標簽獲取。例如,內存的MBeans如下面定義
public interface MemoryMXBean { public MemoryUsage getHeapMemoryUsage(); public MemoryUsage getNonHeapMemoryUsage(); public int getObjectPendingFinalizationCount(); public boolean isVerbose(); public void setVerbose(boolean value); public void gc(); }
內存的MBean包括四個屬性:
HeapMemoryUsage
. 用于描述當前堆內存使用情況的只讀屬性NonHeapMemoryUsage
. 用于描述當前的非堆內存的使用情況的只讀屬性ObjectPendingFinalizationCount
.用于描述有多少對象被掛起以便回收。Verbose
.用于動態設置GC是否跟著詳細的堆棧信息,為一個布爾變量
Figure 3: MBeans Tab.
左邊的樹形結構以名字的方式展示了所有MBeans的列表。一個MBean對象的名字由一個域的名字和一串關鍵字屬性組成。例如,JVM的平臺的MBeans是在“java.lang”域下的一組,而日志的MBeans則在"java.util.logging
"域下。MBean對象的名字在javax.management.ObjectName 規范中定義。
當你在樹中選中一個MBean,屬性,方法,或者通知等一些信息會再右邊顯示出來。如果屬性是可寫的(屬性被標志為藍色),你可以進行設置。你可以操作在Operations tab中列出的操作。你也可以看到由MBean發送出來的通知:默認情況,如果你不訂閱通知的話,JConsole不會收到MBean發生過來的通知。你可以點擊"Subscribe
"(訂閱)按鈕來堆通知進行定義,而使用"Unsubscribe
"按鈕來取消訂閱
Figure 4: MBeans Notification.
監控內存
內存標簽頁通過讀取內存系統、內存池、垃圾回收的MBean來獲取對內存消耗、內存池、垃圾回收的情況的統計。
圖:
上圖展示了內存隨時間變化的使用情況。有對堆的、非堆的以及特殊內存池的統計。內存池信息是否能被獲取,取決與使用的Java虛擬機。下面列表展示了HotSpot虛擬機的內存池情況。
Eden Space (heap): 內存最初從這個線程池分配給大部分對象。
Survivor Space (heap):用于保存在eden space內存池中經過垃圾回收后沒有被回收的對象。
Tenured Generation (heap):用于保持已經在 survivor space內存池中存在了一段時間的對象。
Permanent Generation (non-heap): 保存虛擬機自己的靜態(refective)數據,例如類(class)和方法(method)對象。Java虛擬機共享這些類數據。這個區域被分割為只讀的和只寫的,
Code Cache (non-heap):HotSpot Java虛擬機包括一個用于編譯和保存本地代碼(native code)的內存,叫做“代碼緩存區”(code cache)
詳細信息區域給出一些當前線程的信息:
Used :已使用:當前的內存使用量。使用的內存包括所有對象(能被獲取和不能被獲取的)所占用的內存。
Committed :分配量:Java虛擬機保證能夠獲取到的內存量。分配內存(committedmemory)的量可能隨時間改變。Java虛擬機可能釋放部分這里的內存給系統,相應的分配的內存這時可能少于初始化時分配的給它的量。分配量總數大于或等于已使用的內存量。
Max :內存管理系統可以使用的最大內存量。這個值可以被改變或者不做設定。如果JVM試圖增加使用的內存到大于分配量(committedmemory)的情況,內存分配可能失敗,即便想使用的內存量小于或者等于最大值(如:系統虛擬內存比較低時)
Usage Threshold The usage threshold of a memory pool. This field will only beshown if the memory pool supports usage threshold.
GC time :垃圾回收使用的總時間和調用垃圾回收的次數。它可能有好幾行,每行代表JVM使用的垃圾回收算法。(
右下角的棒狀圖表顯示了被JVM的內存池消耗的內存。如果內存使用超過 usage threshold,則棒會變紅。usagethreshold是用于支持內存檢查的Memory Pool MBean的一個屬性。MemoryPoolMXBean定義了一系列方法用于檢查內存。
public interface MemoryPoolMXBean {
....
// Usage threshold
public long getUsageThreshold();
public void setUsageThreshold(long threshold);
public boolean isUsageThresholdExceeded();
public boolean isUsageThresholdSupported();
// Collection usage threshold
public long getCollectionUsageThreshold();
public void setCollectionUsageThreshold(long threshold);
public boolean isCollectionUsageThresholdSupported();
public boolean isCollectionUsageThresholdExceeded();
}
每種內存池可能有兩種內存初始話支持: usage threshold和collection usage threshold特殊的內存池可能兩種都不支持。
usage threshold是內存池中一個可管理的屬性。它使用低負荷的內存監控。設置usage threshold為正值則usage threshold檢查內存池。設置usage threshold為零,則關閉檢查。默認值由JVM設置。JVM一般讓usage threshold在最合適的時候檢查內存,典型的在GC的過程中和某些分配內存的時候。如果JVM發現當前的內存使用超過了usage threshold,它將會把UsageThresholdExceeded
屬性設置為true
有些內存池可能不支持usage threshold。你可以使用UsageThresholdSupported屬性來判斷一個內存池是否支持
usage threshold。例 如,一個比較完善(generational garbage collector)的垃圾回收器(如HotSpot的虛擬機),most of the objects are allocated in the young generation,從eden內存池中產生。eden pool被設計成可以被裝滿;再eden pool中執行垃圾回收將會釋放他
Collection usage threshold是可進行垃圾回收的內存池的一個可配置屬性。JVM堆一個內存池進行 垃圾回收以后,此內存池中的一些內存仍然被那些沒有被回收的對象占用。collection usage threshold僅允許你在垃圾回收后對內存進行檢查。如果JVM發現可用內存超出collection usage threshold,它將會設置CollectionUsageThresholdExceeded
屬性為true。
你可以使用CollectionUsageThresholdSupported
屬性來控制內存池釋放支持 collection usage threshold.
usage threshold 和collection usage threshold是MBean標簽中的一組。例如,在左邊的樹形結構中選擇TenuredGen,設置tenured generation memory pool的usage threshold為6m。如下圖所示
Figure 6: Setting Usage Threshold.
當 TenuredGen
memory pool的內存使用超過6MBytes時,代表 TenuredGen
memory pool的柱狀圖將會呈現紅色來代表使用的內存超過了usage threshold。代表堆內存的柱狀圖也將變為紅色。你可以選擇柱狀圖或者在圖表中指定內存池來查看某個指定內存池的信息。如果把鼠標房子柱狀圖上,將會顯示出內存池的名字
Figure 7: Low Memory.
開啟/關閉虛擬機的詳細跟蹤
如上所述,內存系統的MBean定義了一個叫做Verbose布爾變量,讓你能動態的打開或關閉詳細的GC跟蹤。詳細的GC跟蹤,將會在JVM啟動時顯示。默認的HotSpot的GC詳細輸出為stdout
.
Figure 8: Setting Verbose GC.
死鎖檢查
線程標簽頁提供關于應用的線程運行信息
Figure 9: Threads Tab.
左下角列出了所以正在運行的線程。如果你在過濾器中輸入一個字符,線程列表將僅顯示線程名字包含你輸入字符的線程。通過點擊某個線程,你可以獲取這個線程的相關信息。
線程的MBean標簽提供了一些Thread標簽沒有提供有用的操作。
findMonitorDeadlockedThreads
. 如果發生線程死鎖,可以通過這個檢查出來。操作返回一組死鎖的線程IDgetThreadInfo
. 返回線程的信息。包括線程的名稱、堆棧信息,導致當前線程阻塞的鎖,如果有的話,還返回哪兒線程持有這個鎖,和這個線程信息的統計。getThreadCpuTime
.返回指定線程消耗的CPU時間。
Figure 10: MBeans Tab Threading.
為檢查你的應用是否進入死鎖(例如,你的應用掛起),你可以使用findMonitorDeadlockedThreads
操作。
Figure 11: Find Deadlocked Threads.
一旦你選擇了findMonitorDeadlockedThreads
按鈕,將會有一個彈出窗口顯示結果。在上面例子中,JConsole連接了一個存在3個死鎖線程的示例應用SampleTest。如上所示,檢查出ID為12,10和11的線程死鎖。想查詢更多的線程信息,可以使用getThreadInfo
操作。線程的MBean支持getThreadInfo
操作的四種形式,
- 對一個給定的線程ID,給出最深的堆棧情況
- 堆一系列的線程ID,給出最深的堆棧情況
- Of a given thread ID with no stack trace.
- Of an array of thread IDs with no stack trace.
Figure 12: ThreadInfo for Thread ID = 12.
雙擊stackTrace屬性的值域將會顯示一個復合對話框,你可以在堆棧中來回查看。圖13,14顯示了死鎖線程-1的復合對話框中的第一層堆棧和第二層堆棧。
Figure 13: Top Frame of the Stack Trace of DeadlockedThread-1.
Figure 14: Second Frame of the Stack Trace of DeadlockedThread-1.
線程標簽頁提供了一個友好的界面供查看線程的堆棧。你可以找到死鎖線程的名字,使用getThreadInfo
查找線程信息。然后又可以使用線程標簽頁來分析死鎖。
控制日志等級
Logging MBean定義了LoggerNames
屬性,用于描述日志名稱。為找到你的應用的日志,可以選擇在MBeans樹中java.util.logging
下的Logging MBean,雙擊LoggerNames屬性
Figure 15: List of All Logger Names.
Logging MBean也支持三種操作:
getParentLoggerName
. 返回指定logger的父loggergetLoggerLevel
. 返回指定logger的日志等級setLoggerLevel
.設置指定logger到一個新的等級
Figure 16: Setting Log Level.
獲取操作系統資源信息-Sun平臺下的擴展
JDK5.0擴展了操作系統的MBean,以此可以獲取一下系統資源的信息,如:
- 處理的CPU
- 總共的和空閑的物理內存
- 可獲得的虛擬內存。(即保證可以分配給運行的進程的虛擬內存)
- 總共的和空閑的交換區
- 打開的文件總數(只能在Unix下使用)
Figure 17: MBeans Tab OS.
除此之外,VM標簽和Summary標簽提供了操作系統資源的一些信息
管理應用的MBean
被監控的SampleTest應用有它自己的Hello MBean:
com.sun.example:type=Hello 如果CacheSize 屬性發生改變,Hello MBean將會發送一個通知。 你可以和管理平臺的MBeans一樣使用MBeans標簽頁來管理你的應用的MBean。 例如,當CacheSize 屬性變化的時候你想監控。你首先可以在 Notification標簽頁中訂閱。如果你改變CacheSize,你可以看到一個通知被發送。
Figure 18: Notifications.
相關信息
JProfiler試用手記
JProfiler是一款Java的性能監控工具。可以查看當前應用的 對象、對象引用、內存、CPU使用情況、線程、線程運行情況(阻塞、等待等),同時可以查找應用內存使用得熱點,即:哪個對象占用的內存比較多;或者CPU熱點,即:哪兒方法占用的較大得CPU資源。我使用的是4.3.2版本,以前試用過3**版本,不過那個bug比較多,容易死,4**版本穩定多了。有了上面那些信息對于系統的調優會有很大幫助。這里提供有幾篇文章供參考:獲取、介紹,簡單入門,使用JProfiler解決實際問題。這幾篇文章基本介紹了常見東西了,下面說點心得。
- JProfiler監控是要消耗系統資源的,所以一般情況下不要用于性能測試時候的監控。
- 如果要用于相對大壓力情況下,可以有選擇的打開監控項,不用所有都打開。主要有兩個,一個是內存監控,打開的情況下可以查找內存分配熱點。一個是CPU監控,打開的情況下可以查看CPU使用熱點。
如圖所示,紅筆標注部分。如果兩個都關閉的話,還是可以跑一定壓力的,同時還可以監控對象數量。 - 個 人認為最好用的(也是用的最多的)是查詢當前的對象的數量。數量監控很重要,如果你使用了單例,那么你只會看到有一個對象存在,如果多了就說明程序有問題 了。同樣,如果應用進行一系列操作,檢查一下該銷毀的對象是否還繼續存在,如果沒有釋放,就得考慮是否存在內存溢出了。
- JProfiler還提供了一個比較好的檢查內存溢出得工具。他可以查找某個對象的引用情況,即:當你發現某個該釋放掉的對象沒有釋放,就可以看一下哪個實例在引用它,找到了根即找到了溢出點。
具體操作如下:在 “Memory Views”界面中右鍵選擇你要監控的對象,選擇第一項“Take Heap Snapshot for Selection”,選擇完成后會進入“Heap Walker”界面,界面下面提供幾個功能,選擇“References”即可 。如圖: - JProfiler提供不同的觀察粒度,提供對類的監控、對包的監控、對J2EE組件的監控,同時過濾器也比較好用,直接定位你關注的包或類即可。
- JProfiler的監控可能與應用之間存在一定時間差,所以有些時候需要等待刷新,才能顯示正確系統情況。