定位問題,知識儲備是基礎,日志等數據是依據,工具則是幫助我們事半功倍的手段。
本文是在win下測試,主要介紹一些工具的使用。
1.jps:虛擬機進程狀況工具
JVM Process Status Tool
可以列出正在運行的虛擬機進程,並顯示虛擬機執行主類(Main Class,main()函數所在的類)名稱以及這些進程的本地虛擬機唯一ID(LVMID,Local Virtual Machine Identifier)。
C:\Users\***\Desktop\64124-深入理解Java虛擬機:JVM高級特性與最佳實踐(第3版)_源碼>jps -l 39172 sun.tools.jps.Jps 24396 24476 org.jetbrains.jps.cmdline.Launcher

2.jstat:虛擬機統計信息工具
JVM Statistics Monitoring Tool
jstat(JVM Statistics Monitoring Tool)是用於監視虛擬機各種運行狀態信息的命令行工具。它可以顯示本地或者遠程[插圖]虛擬機進程中的類加載、內存、垃圾收集、即時編譯等運行時數據,在沒有GUI圖形界面、只提供了純文本控制台環境的服務器上,它將是運行期定位虛擬機性能問題的常用工具。
C:\Users\***\Desktop\64124-深入理解Java虛擬機:JVM高級特性與最佳實踐(第3版)_源碼>jstat -gcutil 24476 S0 S1 E O M CCS YGC YGCT FGC FGCT GCT 75.50 0.00 76.86 0.20 97.04 92.93 2 0.009 0 0.000 0.009
查詢結果表明:這台服務器的新生代Eden區(E,表示Eden)使用了76.8%的空間,2個Survivor區(S0、S1,表示Survivor0、Survivor1),老年代(O,表示Old)和永久代(P,表示Permanent)則分別使用了0.2%和97.4%的空間(這里沒有P只有M,對照看的話應該就是P)。程序運行以來共發生MinorGC(YGC,表示Young GC)2次,總耗時0.1009秒;發生Full GC(FGC,表示Full GC)0次,總耗時(FGCT,表示Full GC Time)為0秒;所有GC總耗時(GCT,表示GC Time)為0.009秒。

3.jinfo:Java配置信息
configuration info for java
這里也就是查-XX:....這類的配置
C:\Users\***\Desktop\64124-深入理解Java虛擬機:JVM高級特性與最佳實踐(第3版)_源碼>jinfo -flag CMSInitiatingOccupancyFraction 24476 -XX:CMSInitiatingOccupancyFraction=-1
4.jmap:java內存映像工具
memory map for java
jmap(Memory Map for Java)命令用於生成堆轉儲快照(一般稱為heapdump或dump文件)。如果不使用jmap命令,要想獲取Java堆轉儲快照也還有一些比較“暴力”的手段:譬如在第2章中用過的-XX:+HeapDumpOnOutOfMemoryError參數,可以讓虛擬機在內存溢出異常出現之后自動生成堆轉儲快照文件,通過-XX:+HeapDumpOnCtrlBreak參數則可以使用[Ctrl]+[Break]鍵讓虛擬機生成堆轉儲快照文件,又或者在Linux系統下通過Kill-3命令發送進程退出信號“恐嚇”一下虛擬機,也能順利拿到堆轉儲快照。對於IDEA的話還有快捷鍵。
C:\Users\***\Desktop\64124-深入理解Java虛擬機:JVM高級特性與最佳實踐(第3版)_源碼>jmap -dump:format=b,file=eclipse.bin 24476 Dumping heap to C:\Users\hufan\Desktop\64124-深入理解Java虛擬機:JVM高級特性與最佳實踐(第3版)_源碼\eclipse.bin ... Heap dump file created

5.jhat:堆轉儲快照分析
jvm heap analysis tool
主要是分析jmap的堆快照,基本沒人用
C:\Users\***\Desktop\64124-深入理解Java虛擬機:JVM高級特性與最佳實踐(第3版)_源碼>jhat eclipse.bin Reading from eclipse.bin... Dump file created Mon Feb 03 13:25:03 CST 2020 Snapshot read, resolving... Resolving 3026440 objects... Chasing references, expect 605 dots............................................................................................................................ ............................................................................................................................................................... ............................................................................................................................................................... ............................................................................................................................................................... .... Eliminating duplicate references............................................................................................................................... ............................................................................................................................................................... ............................................................................................................................................................... ............................................................................................................................................................... . Snapshot resolved. Started HTTP server on port 7000 Server is ready.
這時打開瀏覽器輸入 http://localhost:7000/ 可以看分析結果了。
6.jsatck:java堆棧跟蹤工具
stack trace for java
這個主要是查看各個線程堆棧
C:\Users\***\Desktop\64124-深入理解Java虛擬機:JVM高級特性與最佳實踐(第3版)_源碼>jstack 24396 2020-02-03 13:29:05 Full thread dump OpenJDK 64-Bit Server VM (25.202-b44 mixed mode): "JobScheduler FJ pool 4/7" #170 daemon prio=4 os_prio=-1 tid=0x00000000242dd000 nid=0x2d6c waiting on condition [0x000000004662f000] java.lang.Thread.State: TIMED_WAITING (parking) at sun.misc.Unsafe.park(Native Method) - parking to wait for <0x00000000e33ce9b0> (a java.util.concurrent.ForkJoinPool) at java.util.concurrent.ForkJoinPool.awaitWork(ForkJoinPool.java:1824) at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1693) at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157) "JobScheduler FJ pool 3/7" #169 daemon prio=4 os_prio=-1 tid=0x00000000242dc800 nid=0x9770 waiting on condition [0x000000004652f000] java.lang.Thread.State: WAITING (parking) at sun.misc.Unsafe.park(Native Method) - parking to wait for <0x00000000e33ce9b0> (a java.util.concurrent.ForkJoinPool) at java.util.concurrent.ForkJoinPool.awaitWork(ForkJoinPool.java:1824) at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1693) at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)
從JDK 5起,java.lang.Thread類新增了一個getAllStackTraces()方法用於獲取虛擬機中所有線程的StackTraceElement對象。使用這個方法可以通過簡單的幾行代碼完成jstack的大部分功能。
java的一些命令行工具,可以自行google,這種東西沒必要記,只需要在大腦里有印象即可。
介紹兩個可視化工具
jhsdb,這個工具貌似只有openjdk才有。
案例分析。
/** * staticObj、instanceObj、localObj存放在哪里? */ public class JHSDBTestCase { static class Test { static ObjectHolder staticObj = new ObjectHolder(); ObjectHolder instanceObj = new ObjectHolder(); void foo() { ObjectHolder localObj = new ObjectHolder(); System.out.println("done"); // 這里設一個斷點 } } private static class ObjectHolder {} public static void main(String[] args) { Test test = new JHSDBTestCase.Test(); test.foo(); } }
這個不用工具分析也可以知道,staticObj隨着Test的類型信息存放在方法區,instanceObj隨着Test的對象實例存放在Java堆,localObject則是存放在foo()方法棧幀的局部變量表中。
這里我們用工具分析
1.命令行進入圖形化界面
jhsdb hsdb --pid 11180(你當前運行的線程)

2.點擊菜單中的Tools->Heap Parameters[插圖],結果如圖4-5所示,因為筆者的運行參數中指定了使用的是Serial收集器,圖中我們看到了典型的Serial的分代內存布局,Heap Parameters窗口中清楚列出了新生代的Eden、S1、S2和老年代的容量(單位為字節)以及它們的虛擬內存地址起止范圍。

3.使用scanoops命令在Java堆的新生代(從Eden起始地址到To Survivor結束地址)范圍內查找ObjectHolder的實例,結果如下所示:

可見這三個對象全部落在了新生代Eden,我們繼續分析,再使用revptrs

這個命令大致的作用就是通過反推實例地址,然后我們再用Tool里的inspector工具

JDK 7及其以后版本的HotSpot虛擬機選擇把靜態變量與類型在Java語言一端的映射Class對象存放在一起,存儲於Java堆之中,從我們的實驗中也明確驗證了這一點[插圖]。
然后第二個對象

這次找到一個類型為JHSDB_TestCase$Test的對象實例,在Inspector中該對象實例顯示如圖4-8所示。

第三個方法棧卻不行了。
看來revptrs命令並不支持查找棧上的指針引用,不過沒有關系,得益於我們測試代碼足夠簡潔,人工也可以來完成這件事情。在Java Thread窗口選中main線程后點擊Stack Memory按鈕查看該線程的棧內存,如圖4-9所示。

肉眼看!
參考周志明老師《深入理解Java虛擬機》
