天天看點

JVM性能監控與故障處理工具1.jps:虛拟機程序狀況工具2.jstat:虛拟機統計資訊監視工具3.jinfo:Java配置資訊工具4.jmap:Java記憶體映像工具5.jstack:Java堆棧跟蹤工具

目錄

1.jps:虛拟機程序狀況工具

2.jstat:虛拟機統計資訊監視工具

3.jinfo:Java配置資訊工具

4.jmap:Java記憶體映像工具

5.jstack:Java堆棧跟蹤工具

當我們給一個系統定位問題的時候,知識、經驗是關鍵基礎,資料是依據,工具是運用知識處理資料的手段。這裡的資料包括:運作日志、異常堆棧、GC日志、線程快照、堆轉儲快照等。使用适當的虛拟機監控和分析工具可以加快我們分析資料、定位解決問題的速度。

JDK的工具位于bin目錄下,這些工具大部分是jdk/lib/tools.jar類庫的一層包裝,它們主要的功能代碼是在tools類庫下面實作的。JDK開發團隊選擇采用Java代碼來實作這些監控工具是有特别的用意的:當應用程式部署到生産環境之後,無論是直接接觸實體伺服器還是遠端Telnet到伺服器上都可能會受到限制。借助tools.jar類庫裡面的接口,我們可以直接在應用程式中實作功能強大的監控分析功能。Sun JDK監控和故障處理工具如下表所示:

性能監控與故障處理工具

名稱 主要功能
jps JVM Process Status Tool,顯示指定系統内所有的HotSpot虛拟機程序。
jstat JVM Statistics Monitoring Tool,用于收集HotSpot虛拟機各方面的運作參數。
jinfo Configuration Info for Java,顯示虛拟機的配置資訊。
jmap Memory Map for Java,生成虛拟機的記憶體轉儲(heap dump檔案)快照。
jhat JVM Heap Dump Browser,用于分析heap dump檔案,它會建立一個HTTP/HTML伺服器,讓使用者可以在浏覽器上檢視分析結果。
jstack Stack Trace for Java,顯示虛拟機的線程快照。

1.jps:虛拟機程序狀況工具

jps可以列出正在運作的虛拟機程序,并顯示虛拟機執行的主類(Main Class,main()函數所在的類)名稱以及這些程序的本地虛拟機唯一ID(LVMID)。雖然功能比較單一,但它是使用頻率最高的JDK指令行工具,因為其他的JDK工具大多需要輸入它查詢到的LVMID來确定要監控的是哪一個虛拟機程序。對于本地虛拟機程序來說,LVMID與作業系統的程序ID(PID)是一緻的。jps的指令格式如下:

jps [ options ] [ hostid ]
           

jps的執行樣例如下:

JVM性能監控與故障處理工具1.jps:虛拟機程式狀況工具2.jstat:虛拟機統計資訊監視工具3.jinfo:Java配置資訊工具4.jmap:Java記憶體映像工具5.jstack:Java堆棧跟蹤工具

jps可以通過RMI協定查詢開啟了RMI服務的遠端虛拟機程序狀态,hostid為RMI系統資料庫中注冊的主機名。jps的其他常用選項見下表:

jps工具主要選項

選項 作用
-q 隻輸出LVMID,省略主類的名稱
-m 輸出虛拟機程序啟動時傳遞給主類main()函數的參數
-l 輸出主類的全名,如果程序執行的是jar包,輸出Jar路徑。
-v 輸出虛拟機程序啟動時JVM參數。

2.jstat:虛拟機統計資訊監視工具

jstat是用于監視虛拟機各種運作狀态資訊的指令行工具。它可以顯示本地或者遠端虛拟機程序中的類裝載、記憶體、垃圾收集、JIT編譯等運作資料,在沒有GUI圖形界面,隻提供了純文字控制台環境的伺服器上,它将是運作期定位虛拟機性能問題的首選工具。jstat指令格式為:

   jstat [option vmid [interval [s|ms] [count]]   ]
           

對于指令格式中的vmid 和 LVMID,如果是本地虛拟機程序,VMID與LVMID是一緻的,如果是遠端的虛拟機程序,那麼VMID的格式為:

 [protocol:][//]lvmid[@hostname[:port]/servername]
           

參數interval和count代表查詢間隔和次數,如果省略這兩個參數,說明隻查詢一次。假設需要每250ms查詢一次程序2764垃圾收集狀況,一共查詢200次,那麼指令應該是:

 jstat -gc 2764 250 20
           

選項option代表着使用者希望查詢的虛拟機資訊,主要分為3類:類裝載、垃圾收集、運作期編譯狀況,具體選項及作用參考如下表:

jstat工具的主要選項

選項 作用
-class 監視類裝載、解除安裝數量、總空間以及類裝載所消耗的時間。
-gc 監視Java堆狀況,包括Eden區、兩個Survivor區、老年代、永久代等的容量,已用空間、GC時間合計等資訊。
-gccapacity 監視内容和-gc基本相同,但輸出主要關注Java堆各個區域使用到的最大、最小空間。
-gcutil 監視内容和-gc基本相同,但輸出主要關注已使用空間占總空間的百分比。
-gccause 與-gcutil功能一緻,但是會額外輸出導緻上一次GC産生的原因。
-gcnew 監視新生代GC狀況。
-gcnewcapacity 監視内容與-gcnew基本相同,但是輸出主要關注使用到的最大,最小空間。
-gcold 監視老年代GC狀況。
-gcoldcapacity 監視内容與-gcold基本相同,但是輸出主要關注使用到的最大,最小空間。
-gcpermcapacity 輸出永久代使用到的最大、最小空間。
-compiler 輸出JIT編譯器編譯過的方法、耗時等資訊。
-printcompilation 屬于已經被JIT編譯的方法。

jstat的執行執行個體如下:

JVM性能監控與故障處理工具1.jps:虛拟機程式狀況工具2.jstat:虛拟機統計資訊監視工具3.jinfo:Java配置資訊工具4.jmap:Java記憶體映像工具5.jstack:Java堆棧跟蹤工具

查詢結果表明:這台伺服器的新生代Eden區(E,表示Eden)使用了99.02%的空間,兩個Survivor區(S0、S1,Survivor0表示使用了38.02%、Survivor1裡面是空的),老年代(O,表示Old)和永久代(P,表示Permanent)則表示使用了28.42%和40.90%的空間。程式運作以來共發生Minor GC(YGC,表示Young GC)74次,總耗時0.810秒,發生Full GC(FGC,表示Full GC)0次,是以對應的Full GC總耗時(FGCT)為0秒,所有GC的總耗時(GCT)為0.810秒。

3.jinfo:Java配置資訊工具

jinfo的作用是實時地檢視和調整虛拟機各項參數。使用jps指令的-v參數可以檢視虛拟機啟動時顯示指定的參數清單,但是如果想知道未被顯式指定的參數的系統預設值,就隻能使用jinfo的-flags選項進行查詢了。jinfo還可以使用-sysprops選項把虛拟機程序的System.getProperties()的内容列印出來。在jdk1.6以後,jinfo可以使用-flag[+|-]name或者-flag name=value修改一部分運作期可寫的虛拟機參數值。jinfo的指令格式如下:

 jinfo [option] pid
           

執行樣例,查詢Java程式的虛拟機參數 : 

JVM性能監控與故障處理工具1.jps:虛拟機程式狀況工具2.jstat:虛拟機統計資訊監視工具3.jinfo:Java配置資訊工具4.jmap:Java記憶體映像工具5.jstack:Java堆棧跟蹤工具

查詢永久帶PermSize大小的:

JVM性能監控與故障處理工具1.jps:虛拟機程式狀況工具2.jstat:虛拟機統計資訊監視工具3.jinfo:Java配置資訊工具4.jmap:Java記憶體映像工具5.jstack:Java堆棧跟蹤工具

4.jmap:Java記憶體映像工具

jmap(Memory Map for Java)指令用于生成堆轉儲快照,一般稱為heapdump或dump檔案。如果不使用jmap指令,想要獲得Java堆轉儲快照,可以使用以下幾種方式:

  • -XX:+HeapDumpOnOutOfMemoryError參數,可以讓虛拟機在OOM異常出現之後自動生成dump檔案。
  • -XX:+HeapDumpOnCtrlBreak參數則可以使用[Ctrl]+[Break]鍵讓虛拟機生成dump檔案。

jmap的作用并不僅僅是為了擷取dump檔案,它還可以查詢finalize執行隊列、Java堆和永久代的詳細資訊,如空間使用率、目前用的是哪種收集器。jmap指令格式為:

jmap [option] vmid
           

option選項的合法值與具體含義如下表所示:

jmap工具主要選項

選項 作用
-dump 生成java堆轉儲快照,格式為:-dump:[live,]format=b,file=<filenam>,其中live子參數說明是否隻dump出存活的對象。
-finalizerinfo 顯示在F-Queue中等待Finalizer線程執行finalize方法的對象。
-heap 顯示Java堆詳細資訊,如使用哪種回收器、參數配置、分代狀況等。
-histo 顯示堆中對象統計資訊,包括類、執行個體數量和合計容量
-permstat 以ClassLoader為統計口徑顯示永久代記憶體狀态。
-F 當虛拟機程序對-dump選項沒有響應時,可使用這個選項強制生成dump快照。

比如如果要dump檔案,可以執行如下指令:

jmap -dump:format=b,file=/export/App/dump 588802
           

 顯示Java堆的配置情況和使用情況,還有使用的GC算法,可以執行如下指令:

jmap -heap 588802
           

輸出資訊如下:

Attaching to process ID 588802, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.91-b14

using thread-local object allocation.
Parallel GC with 43 thread(s)

Heap Configuration:
   MinHeapFreeRatio         = 0
   MaxHeapFreeRatio         = 100
   MaxHeapSize              = 2147483648 (2048.0MB)
   NewSize                  = 715653120 (682.5MB)
   MaxNewSize               = 715653120 (682.5MB)
   OldSize                  = 1431830528 (1365.5MB)
   NewRatio                 = 2   //表示年輕代和老年代的比例是1比2
   SurvivorRatio            = 8  //年輕代中的Eden和To Survivor、From Survivor的比例是8:1:1
   MetaspaceSize            = 21807104 (20.796875MB)
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   MaxMetaspaceSize         = 17592186044415 MB
   G1HeapRegionSize         = 0 (0.0MB)

Heap Usage:
PS Young Generation
Eden Space:
   capacity = 706215936 (673.5MB)
   used     = 468877672 (447.1565933227539MB)
   free     = 237338264 (226.3434066772461MB)
   66.39296114665983% used
From Space:
   capacity = 4718592 (4.5MB)
   used     = 2031616 (1.9375MB)
   free     = 2686976 (2.5625MB)
   43.05555555555556% used
To Space:
   capacity = 4718592 (4.5MB)
   used     = 0 (0.0MB)
   free     = 4718592 (4.5MB)
   0.0% used
PS Old Generation
   capacity = 1431830528 (1365.5MB)
   used     = 405141792 (386.3733215332031MB)
   free     = 1026688736 (979.1266784667969MB)
   28.295373235679467% used

111671 interned Strings occupying 9653912 bytes.
           

 在JVM的記憶體模型的堆中,堆被劃分為新生代和老年代,新生代又被進一步劃分為Eden區和Survivor區,最後Survivor由From Survivor和To Survivor組成。

5.jstack:Java堆棧跟蹤工具

jstack(Stack Trace for Java)指令用于生成虛拟機目前時刻的線程快照(一般稱為threaddump或者javacore檔案)。線程快照就是目前虛拟機内每一條線程正在執行的java堆棧集合,生成線程快照的主要目的是定位線程出現長時間停頓的原因,如線程間死鎖、死循環、請求外部資源導緻的長時間等待等都是導緻線程長時間停頓的常見原因。線程出現停頓的時候通過jstack來檢視各個線程的調用堆棧,就可以知道沒有響應的線程到底在背景做了些什麼事情,或者等待着什麼資源。jstack指令格式:

jstack [option] vmid
           

執行結果如下:

2020-05-27 19:25:49
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.91-b14 mixed mode):

"localhost-startStop-1-EventThread" #22462 daemon prio=5 os_prio=0 tid=0x00007fbf2c035000 nid=0x55ebc waiting on condition [0x00007fc4250cd000]
   java.lang.Thread.State: WAITING (parking)
	at sun.misc.Unsafe.park(Native Method)
	- parking to wait for  <0x00000000cfb78658> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
	at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
	at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
	at org.apache.zookeeper.ClientCnxn$EventThread.run(ClientCnxn.java:491)

"localhost-startStop-1-SendThread(10.173.77.140:2181)" #22461 daemon prio=5 os_prio=0 tid=0x00007fbf2c01b000 nid=0x55ebb runnable [0x00007fc401c69000]
   java.lang.Thread.State: RUNNABLE
	at sun.nio.ch.EPollArrayWrapper.epollWait(Native Method)
	at sun.nio.ch.EPollArrayWrapper.poll(EPollArrayWrapper.java:269)
	at sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:93)
	at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:86)
	- locked <0x00000000cfb76848> (a sun.nio.ch.Util$2)
	- locked <0x00000000cfb76858> (a java.util.Collections$UnmodifiableSet)
	- locked <0x00000000cfb76800> (a sun.nio.ch.EPollSelectorImpl)
	at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:97)
	at org.apache.zookeeper.ClientCnxnSocketNIO.doTransport(ClientCnxnSocketNIO.java:338)
	at org.apache.zookeeper.ClientCnxn$SendThread.run(ClientCnxn.java:1068)

"localhost-startStop-1-EventThread" #22460 daemon prio=5 os_prio=0 tid=0x00007fc134059000 nid=0x55eba waiting on condition [0x00007fc41d9d8000]
   java.lang.Thread.State: WAITING (parking)
	at sun.misc.Unsafe.park(Native Method)
	- parking to wait for  <0x00000000cfb75b98> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
	at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
	at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
	at org.apache.zookeeper.ClientCnxn$EventThread.run(ClientCnxn.java:491)
           

option選項的合法值與具體含義如下表所示:

jstack工具的主要選項

選項 作用
-F 當正常輸出的請求不被響應時,強制輸出線程堆棧。
-l 出堆棧外,顯示關于鎖的附加資訊。
-m 如果調用到本地方法 的話,可以顯示C/C++的堆棧。

在JDK1.5中,java.lang.Thread類新增了一個getAllStackTraces() 方法用于擷取虛拟機中所有線程的StackTraceElement對象。使用這個方法可以通過簡單的幾行代碼就完成jstack的大部分功能,在實際的項目中可以調用這個方法做一個管理者的界面,可以随時使用浏覽器來檢視線程堆棧,如下面的jsp代碼:

<%@ page import="java.util.Map" %>
<html>
<body>
<h2>Hello World!</h2>
<pre>
    <%
        for (Map.Entry<Thread, StackTraceElement[]> stackTrace : Thread.getAllStackTraces().entrySet()) {
            Thread thread = (Thread) stackTrace.getKey();
            StackTraceElement[] stack = (StackTraceElement[]) stackTrace.getValue();
            if (thread.equals(Thread.currentThread())) {
                continue;
            }
            out.print("\n線程:" + thread.getName() + "\n");
            for (StackTraceElement element : stack) {
                out.print("\t" + element + "\n");
            }
        }
    %>
</pre>
</body>
</html>