這可能是把JDK監(jiān)控和故障處理工具總結(jié)的最完整的一篇文章了
一、JDK 命令行工具
這些命令在 JDK 安裝目錄下的 bin 目錄下:
- jps (JVM Process Status): 類似 UNIX 的 ps 命令。用戶查看所有 Java 進程的啟動類、傳入?yún)?shù)和 Java 虛擬機參數(shù)等信息;
- jstat( JVM Statistics Monitoring Tool): 用于收集 HotSpot 虛擬機各方面的運行數(shù)據(jù);
- jinfo (Configuration Info for Java) : Configuration Info forJava,顯示虛擬機配置信息;
- jmap (Memory Map for Java) :生成堆轉(zhuǎn)儲快照;
- jhat (JVM Heap Dump Browser ) : 用于分析 heapdump 文件,它會建立一個 HTTP/HTML 服務(wù)器,讓用戶可以在瀏覽器上查看分析結(jié)果;
- jstack (Stack Trace for Java):生成虛擬機當(dāng)前時刻的線程快照,線程快照就是當(dāng)前虛擬機內(nèi)每一條線程正在執(zhí)行的方法堆棧的集合。
1. jps:查看所有 Java 進程
jps(JVM Process Status) 命令類似 UNIX 的 ps 命令。
jps:顯示虛擬機執(zhí)行主類名稱以及這些進程的本地虛擬機唯一 ID(Local Virtual Machine Identifier,LVMID)。jps -q :只輸出進程的本地虛擬機唯一 ID。
- C:\Users\SnailClimb>jps7360 NettyClient2173967972 Launcher16504 Jps17340 NettyServer
jps -l:輸出主類的全名,如果進程執(zhí)行的是 Jar 包,輸出 Jar 路徑。
- C:\Users\SnailClimb>jps -l7360 firstNettyDemo.NettyClient2173967972 org.jetbrains.jps.cmdline.Launcher16492 sun.tools.jps.Jps17340 firstNettyDemo.NettyServer
jps -v:輸出虛擬機進程啟動時 JVM 參數(shù)。
jps -m:輸出傳遞給 Java 進程 main() 函數(shù)的參數(shù)。
2. jstat: 監(jiān)視虛擬機各種運行狀態(tài)信息
jstat(JVM Statistics Monitoring Tool) 使用于監(jiān)視虛擬機各種運行狀態(tài)信息的命令行工具。 它可以顯示本地或者遠程(需要遠程主機提供 RMI 支持)虛擬機進程中的類信息、內(nèi)存、垃圾收集、JIT 編譯等運行數(shù)據(jù),在沒有 GUI,只提供了純文本控制臺環(huán)境的服務(wù)器上,它將是運行期間定位虛擬機性能問題的首選工具。
jstat 命令使用格式:
- jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]
比如 jstat -gc -h3 31736 1000 10表示分析進程 id 為 31736 的 gc 情況,每隔 1000ms 打印一次記錄,打印 10 次停止,每 3 行后打印指標(biāo)頭部。
常見的 option 如下:
- jstat -class vmid :顯示 ClassLoader 的相關(guān)信息;
- jstat -compiler vmid :顯示 JIT 編譯的相關(guān)信息;
- jstat -gc vmid :顯示與 GC 相關(guān)的堆信息;
- jstat -gccapacity vmid :顯示各個代的容量及使用情況;
- jstat -gcnew vmid :顯示新生代信息;
- jstat -gcnewcapcacity vmid :顯示新生代大小與使用情況;
- jstat -gcold vmid :顯示老年代和永久代的信息;
- jstat -gcoldcapacity vmid :顯示老年代的大?。?/li>
- jstat -gcpermcapacity vmid :顯示永久代大??;
- jstat -gcutil vmid :顯示垃圾收集信息;
另外,加上 -t參數(shù)可以在輸出信息上加一個 Timestamp 列,顯示程序的運行時間。
3. jinfo: 實時地查看和調(diào)整虛擬機各項參數(shù)
jinfo vmid :輸出當(dāng)前 jvm 進程的全部參數(shù)和系統(tǒng)屬性 (第一部分是系統(tǒng)的屬性,第二部分是 JVM 的參數(shù))。
jinfo -flag name vmid :輸出對應(yīng)名稱的參數(shù)的具體值。比如輸出 MaxHeapSize、查看當(dāng)前 jvm 進程是否開啟打印 GC 日志 ( -XX:PrintGCDetails :詳細 GC 日志模式,這兩個都是默認(rèn)關(guān)閉的)。
- C:\Users\SnailClimb>jinfo -flag MaxHeapSize 17340-XX:MaxHeapSize=2124414976C:\Users\SnailClimb>jinfo -flag PrintGC 17340-XX:-PrintGC
使用 jinfo 可以在不重啟虛擬機的情況下,可以動態(tài)的修改 jvm 的參數(shù)。尤其在線上的環(huán)境特別有用,請看下面的例子:
jinfo -flag [+|-]name vmid 開啟或者關(guān)閉對應(yīng)名稱的參數(shù)。
- C:\Users\SnailClimb>jinfo -flag PrintGC 17340-XX:-PrintGCC:\Users\SnailClimb>jinfo -flag +PrintGC 17340C:\Users\SnailClimb>jinfo -flag PrintGC 17340-XX:+PrintGC
4. jmap:生成堆轉(zhuǎn)儲快照
jmap(Memory Map for Java)命令用于生成堆轉(zhuǎn)儲快照。 如果不使用 jmap 命令,要想獲取 Java 堆轉(zhuǎn)儲,可以使用 “-XX:+HeapDumpOnOutOfMemoryError” 參數(shù),可以讓虛擬機在 OOM 異常出現(xiàn)之后自動生成 dump 文件,Linux 命令下可以通過 kill -3 發(fā)送進程退出信號也能拿到 dump 文件。
jmap 的作用并不僅僅是為了獲取 dump 文件,它還可以查詢 finalizer 執(zhí)行隊列、Java 堆和永久代的詳細信息,如空間使用率、當(dāng)前使用的是哪種收集器等。和jinfo一樣,jmap有不少功能在 Windows 平臺下也是受限制的。
示例:將指定應(yīng)用程序的堆快照輸出到桌面。后面,可以通過 jhat、Visual VM 等工具分析該堆文件。
- C:\Users\SnailClimb>jmap -dump:format=b,file=C:\Users\SnailClimb\Desktop\heap.hprof 17340Dumping heap to C:\Users\SnailClimb\Desktop\heap.hprof ...Heap dump file created

5. jhat: 分析 heapdump 文件
jhat 用于分析 heapdump 文件,它會建立一個 HTTP/HTML 服務(wù)器,讓用戶可以在瀏覽器上查看分析結(jié)果。
- C:\Users\SnailClimb>jhat C:\Users\SnailClimb\Desktop\heap.hprofReading from C:\Users\SnailClimb\Desktop\heap.hprof...Dump file created Sat May 04 12:30:31 CST 2019Snapshot read, resolving...Resolving 131419 objects...Chasing references, expect 26 dots..........................Eliminating duplicate references..........................Snapshot resolved.Started HTTP server on port 7000Server is ready.
6. jstack :生成虛擬機當(dāng)前時刻的線程快照
jstack(Stack Trace for Java)命令用于生成虛擬機當(dāng)前時刻的線程快照。線程快照就是當(dāng)前虛擬機內(nèi)每一條線程正在執(zhí)行的方法堆棧的集合.
生成線程快照的目的主要是定位線程長時間出現(xiàn)停頓的原因,如線程間死鎖、死循環(huán)、請求外部資源導(dǎo)致的長時間等待等都是導(dǎo)致線程長時間停頓的原因。線程出現(xiàn)停頓的時候通過jstack來查看各個線程的調(diào)用堆棧,就可以知道沒有響應(yīng)的線程到底在后臺做些什么事情,或者在等待些什么資源。
下面是一個線程死鎖的代碼。我們下面會通過 jstack 命令進行死鎖檢查,輸出死鎖信息,找到發(fā)生死鎖的線程。
- public class DeadLockDemo { private static Object resource1 = new Object();//資源 1 private static Object resource2 = new Object();//資源 2 public static void main(String[] args) { new Thread(() -> { synchronized (resource1) { System.out.println(Thread.currentThread() + "get resource1"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread() + "waiting get resource2"); synchronized (resource2) { System.out.println(Thread.currentThread() + "get resource2"); } } }, "線程 1").start(); new Thread(() -> { synchronized (resource2) { System.out.println(Thread.currentThread() + "get resource2"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread() + "waiting get resource1"); synchronized (resource1) { System.out.println(Thread.currentThread() + "get resource1"); } } }, "線程 2").start(); }}
Output
- Thread[線程 1,5,main]get resource1Thread[線程 2,5,main]get resource2Thread[線程 1,5,main]waiting get resource2Thread[線程 2,5,main]waiting get resource1
線程 A 通過 synchronized (resource1) 獲得 resource1 的監(jiān)視器鎖,然后通過Thread.sleep(1000);讓線程 A 休眠 1s 為的是讓線程 B 得到執(zhí)行然后獲取到 resource2 的監(jiān)視器鎖。線程 A 和線程 B 休眠結(jié)束了都開始企圖請求獲取對方的資源,然后這兩個線程就會陷入互相等待的狀態(tài),這也就產(chǎn)生了死鎖。
通過 jstack 命令分析:
- C:\Users\SnailClimb>jps13792 KotlinCompileDaemon7360 NettyClient2173967972 Launcher8932 Launcher9256 DeadLockDemo10764 Jps17340 NettyServerC:\Users\SnailClimb>jstack 9256
輸出的部分內(nèi)容如下:
- Found one Java-level deadlock:============================="線程 2": waiting to lock monitor 0x000000000333e668 (object 0x00000000d5efe1c0, a java.lang.Object), which is held by "線程 1""線程 1": waiting to lock monitor 0x000000000333be88 (object 0x00000000d5efe1d0, a java.lang.Object), which is held by "線程 2"Java stack information for the threads listed above:==================================================="線程 2": at DeadLockDemo.lambda$main$1(DeadLockDemo.java:31) - waiting to lock <0x00000000d5efe1c0> (a java.lang.Object) - locked <0x00000000d5efe1d0> (a java.lang.Object) at DeadLockDemo$$Lambda$2/1078694789.run(Unknown Source) at java.lang.Thread.run(Thread.java:748)"線程 1": at DeadLockDemo.lambda$main$0(DeadLockDemo.java:16) - waiting to lock <0x00000000d5efe1d0> (a java.lang.Object) - locked <0x00000000d5efe1c0> (a java.lang.Object) at DeadLockDemo$$Lambda$1/1324119927.run(Unknown Source) at java.lang.Thread.run(Thread.java:748)Found 1 deadlock.
可以看到 jstack 命令已經(jīng)幫我們找到發(fā)生死鎖的線程的具體信息。
二、JDK 可視化分析工具
1. JConsole:Java 監(jiān)視與管理控制臺
JConsole 是基于 JMX 的可視化監(jiān)視、管理工具??梢院芊奖愕谋O(jiān)視本地及遠程服務(wù)器的 java 進程的內(nèi)存使用情況。你可以在控制臺輸出console命令啟動或者在 JDK 目錄下的 bin 目錄找到j(luò)console.exe然后雙擊啟動。
①. 連接 Jconsole
如果需要使用 JConsole 連接遠程進程,可以在遠程 Java 程序啟動時加上下面這些參數(shù):
- -Djava.rmi.server.hostname=外網(wǎng)訪問 ip 地址 -Dcom.sun.management.jmxremote.port=60001 //監(jiān)控的端口號-Dcom.sun.management.jmxremote.authenticate=false //關(guān)閉認(rèn)證-Dcom.sun.management.jmxremote.ssl=false
在使用 JConsole 連接時,遠程進程地址如下:
- 外網(wǎng)訪問 ip 地址:60001
②. 內(nèi)存監(jiān)控
JConsole 可以顯示當(dāng)前內(nèi)存的詳細信息。不僅包括堆內(nèi)存/非堆內(nèi)存的整體信息,還可以細化到 eden 區(qū)、survivor 區(qū)等的使用情況,如下圖所示。
點擊右邊的“執(zhí)行 GC(G)”按鈕可以強制應(yīng)用程序執(zhí)行一個 Full GC。
新生代 GC(Minor GC):指發(fā)生新生代的的垃圾收集動作,Minor GC 非常頻繁,回收速度一般也比較快。 老年代 GC(Major GC/Full GC):指發(fā)生在老年代的 GC,出現(xiàn)了 Major GC 經(jīng)常會伴隨至少一次的 Minor GC(并非絕對),Major GC 的速度一般會比 Minor GC 的慢 10 倍以上。
③. 線程監(jiān)控
類似我們前面講的 jstack 命令,不過這個是可視化的。
最下面有一個"檢測死鎖 (D)"按鈕,點擊這個按鈕可以自動為你找到發(fā)生死鎖的線程以及它們的詳細信息 。
2. Visual VM:多合一故障處理工具
VisualVM 提供在 Java 虛擬機 (Java Virutal Machine, JVM) 上運行的 Java 應(yīng)用程序的詳細信息。在 VisualVM 的圖形用戶界面中,您可以方便、快捷地查看多個 Java 應(yīng)用程序的相關(guān)信息。
下面這段話摘自《深入理解 Java 虛擬機》。
VisualVM(All-in-One Java Troubleshooting Tool)是到目前為止隨 JDK 發(fā)布的功能最強大的運行監(jiān)視和故障處理程序,官方在 VisualVM 的軟件說明中寫上了“All-in-One”的描述字樣,預(yù)示著他除了運行監(jiān)視、故障處理外,還提供了很多其他方面的功能,如性能分析(Profiling)。VisualVM 的性能分析功能甚至比起 JProfiler、YourKit 等專業(yè)且收費的 Profiling 工具都不會遜色多少,而且 VisualVM 還有一個很大的優(yōu)點:不需要被監(jiān)視的程序基于特殊 Agent 運行,因此他對應(yīng)用程序的實際性能的影響很小,使得他可以直接應(yīng)用在生產(chǎn)環(huán)境中。這個優(yōu)點是 JProfiler、YourKit 等工具無法與之媲美的。
VisualVM 基于 NetBeans 平臺開發(fā),因此他一開始就具備了插件擴展功能的特性,通過插件擴展支持,VisualVM 可以做到:
- 顯示虛擬機進程以及進程的配置、環(huán)境信息(jps、jinfo)。
- 監(jiān)視應(yīng)用程序的 CPU、GC、堆、方法區(qū)以及線程的信息(jstat、jstack)。
- dump 以及分析堆轉(zhuǎn)儲快照(jmap、jhat)。
- 方法級的程序運行性能分析,找到被調(diào)用最多、運行時間最長的方法。
- 離線程序快照:收集程序的運行時配置、線程 dump、內(nèi)存 dump 等信息建立一個快照,可以將快照發(fā)送開發(fā)者處進行 Bug 反饋。
- 其他 plugins 的無限的可能性......