Java內存分析相關工具
1、jps 工具(jdk自帶,列出java進程)
jps(Java Virtual Machine Process Status Tool)是JDK提供的一個可以列出正在運行的Java虛擬機的進程信息的命令行工具,它可以顯示 Java 虛擬機進程的執行主類(Main Class,main()函數所在的類)名稱、本地虛擬機唯一ID(LVMID,Local Virtual Machine Identifier)等信息。另外,jps 命令只能顯示它有訪問權限的Java進程的信息。

命令語法:
jps [-q] [-mlvV] [hostid]
jps [-help]
命令參數說明:
- -q:不顯示主類名稱、JAR文件名和傳遞給主方法的參數,只顯示本地虛擬機唯一ID。
-mlvV:我們可以指定這些參數的任意組合。
- -m:顯示Java虛擬機啟動時傳遞給main()方法的參數。
- -l:顯示主類的完整包名,如果進程執行的是JAR文件,也會顯示JAR文件的完整路徑。
- -v:顯示Java虛擬機啟動時傳遞的JVM參數。
- -V:不顯示主類名稱、JAR文件名和傳遞給主方法的參數,只顯示本地虛擬機唯一ID。
- hostid:指定的遠程主機,可以是ip地址和域名, 也可以指定具體協議,端口。如果不指定,則顯示本機的Java虛擬機的進程信息。
- -help:顯示jps命令的幫助信息。
2、jstack(jdk自帶,打印線程當前堆棧快照)
jstack 是 JVM 自帶的 Java 堆棧跟蹤工具,它用于打印出給定的 java 進程ID、core file、遠程調試服務的Java堆棧信息。jstack 命令用于生成虛擬機當前時刻的線程快照。
命令格式:
jstack [ option ] pid // 打印某個進程的堆棧信息 jstack [ option ] executable core jstack [ option ] [server-id@]remote-hostname-or-IP

3、jmap 工具(jdk自帶,查看堆內存快照)
jmap 命令是 JDK 提供的一個命令行工具,用于生成Java虛擬機的堆轉儲快照(dump文件),也被稱為 heapdump 或 dump 文件。這個命令不僅可以獲取dump文件,還可以查詢finalize執行隊列、Java堆和永久代的詳細信息,如空間使用率、當時使用的是哪種垃圾收集器等。
命令語法:
jmap [options] pid
pid:進程ID。option:jmap命令的可選參數。如果沒有指定該參數,jinfo命令會顯示Java虛擬機進程的內存映像信息
下圖示例:

3.1、jmap 常見參數
以下是 jmap 常見的參數和其作用的簡要講解:
-heap:顯示 Java 堆的配置和使用情況。這個選項會輸出 Java 堆的配置信息,包括堆的大小、各代的大小和使用情況。-histo:顯示 Java 堆中各個類的實例數量和占用內存。這個選項會輸出 Java 堆中各個類的實例數量和占用內存,按照內存大小降序排列。-dump:live,format=b,file=<filename>:生成堆轉儲文件。這個選項用于生成 Java 堆的轉儲文件。live表示只轉儲活動對象,format=b表示使用二進制格式,file=<filename>指定輸出文件名。-F:當進程不響應時,強制生成堆轉儲文件。jmap -F -dump:format=b,file=heap_dump.bin <pid>-F選項用于在 Java 進程不響應時強制生成堆轉儲文件。- -hprof[:]:以 HPROF 格式生成堆轉儲文件,可通過端口進行遠程連接。
jmap -hprof:port=<port> <pid>這個選項以HPROF格式生成堆轉儲文件,并可以通過指定的端口進行遠程連接。 -finalizerinfo:顯示等待終結的對象的信息。這個選項顯示等待終結的對象的信息,有助于識別終結線程是否正常工作。-permstat:顯示永久代的詳細信息。這個選項用于顯示永久代的詳細信息,包括類加載器、類元數據等信息。
以上是一些常見的 jmap 參數,通過這些參數可以獲取關于 Java 進程內存使用情況的詳細信息,用于診斷和解決內存相關的性能問題。
3.1.1、jmap -dump:format=b,file=path(將JVM的heap內容輸出到文件中)
jmap -dump:format=b,file=path PID # 示例 jmap -dump:format=b,file=/usr/msg/20240203_heapdump.hprof 10016 jmap -dump:format=b,file=F:/myHeapdumpTest/20240203_heapdump.hprof 10016
通過以上命令可以將JVM的heap內容輸出到文件中,生成堆轉儲快照,這對于分析內存使用情況和定位內存泄漏問題非常有用。
示例:

注意,通過以上命令獲取 dump 文件必須是一出現內存異常就獲取 dump 文件,這樣獲取的文件信息才比較準確。如果無法及時獲取,可能內存已經被釋放,獲取到的信息對于分析問題意義就不大了。
3.1.2、jmap -heap pid(顯示 Java 堆的配置和使用情況)
顯示Java堆的如下信息:
- 被指定的垃圾回收算法的信息,包括垃圾回收算法的名稱和垃圾回收算法的詳細信息。
- 堆的配置信息,可能是由命令行選項指定,或者由Java虛擬機根據服務器配置選擇的。
- 堆的內存空間使用信息,包括分代情況,每個代的總容量、已使用內存、可使用內存。如果某一代被繼續細分(例如,年輕代),則包含細分的空間的內存使用信息。
代碼演示,通過以下步驟演示使用 jmap 分析代碼執行過程中的堆內存。
package cn.itcast.jvm.t1.heap; /** * 演示堆內存 */ public class Demo1_4 { public static void main(String[] args) throws InterruptedException { System.out.println("1..."); Thread.sleep(30000); byte[] array = new byte[1024 * 1024 * 10]; // 10 Mb System.out.println("2..."); Thread.sleep(20000); array = null; System.gc(); System.out.println("3..."); Thread.sleep(1000000L); } }
以上代碼將依次輸出1、2、3,如下:

我們將依次在程序輸出1、2、3時打印出此時的堆內存。
如下,在輸出 1 時打印的堆內存,可以看到此時程序剛開始啟動,堆內存占用并不大。

在輸出 2 時打印堆內存如下,因為此時已經創建了一個 10m 大小的字節數組對象,所以可以看到堆內存增加了差不多 10m 的大小。

在輸出 3 時打印堆內存如下,因為此時我們沒有代碼指向那個字節數組對象,并且主動調用了垃圾回收,所以可以看到堆內存減少了差不多 10m 的大小,這是因為那個字節數組對象被垃圾回收掉了。

4、jconsole(jdk自帶,監控內存CPU等指標)
Jconsole 是 jdk 自帶的一套java虛擬機執行狀況監視器,它能夠用來監控虛擬機的內存,線程,cpu使用情況以及相關的java進程相關的MBean。jconsole 跟 jmap 不一樣,jconsole 可以一直監控內存等情況,而 jmap 只是打印出當前時刻的堆內存情況,相對來說 jconsole 更好用些。
如果有安裝 jdk 的話,直接在命令中輸入 jconsole 即可彈出 jconsole 窗口,然后輸入連接 ip 等配置即可。主界面示例以下:

演示,假如我們執行以下代碼:
package cn.itcast.jvm.t1.heap; /** * 演示堆內存 */ public class Demo1_4 { public static void main(String[] args) throws InterruptedException { System.out.println("1..."); Thread.sleep(30000); byte[] array = new byte[1024 * 1024 * 10]; // 10 Mb System.out.println("2..."); Thread.sleep(20000); array = null; System.gc(); System.out.println("3..."); Thread.sleep(1000000L); } }
在啟動 main 方法后,在命令行中直接輸入 jconsole,可以看到堆內存的趨勢,如下:

可以看到,堆內存在一個時間點急劇上升,這是因為我們創建了一個接近 10m 的字節數組對象,然后又在一個時間點急劇下降,這是因為此時那個字節數組對象已經沒有引用,并且我們主動調用了垃圾回收,所以可以看到堆內存減少了差不多 10m 的大小,。
啟動 jconsole 時,可以選擇本地連接,然后選擇對應的程序,如下,連接后提示不安全的連接直接點擊確定即可。

5、jvisualvm(jdk自帶,性能分析工具)
jvisualvm 是 JDK 自帶的 JAVA 性能分析工具,它默認已經在你的JDK bin目錄里了,只要你使用的是JDK1.6 Update7之后的版本,點擊一下jvisualvm.exe圖標它就可以運行了,如 E:\Develop\jdk1.8.0_202\bin\jvisualvm.exe 。
如演示分析以下代碼執行垃圾回收后,內存占用仍然很高
package cn.itcast.jvm.t1.heap; import java.util.ArrayList; import java.util.List; /** * 演示查看對象個數 堆轉儲 dump */ public class Demo1_13 { public static void main(String[] args) throws InterruptedException { List<Student> students = new ArrayList<>(); for (int i = 0; i < 200; i++) { students.add(new Student()); // Student student = new Student(); } Thread.sleep(1000000000L); } } class Student { private byte[] big = new byte[1024*1024]; }
啟動上面 java 程序后,在命令行輸入 jvisualvm 打開分析工具,如下:


6、MAT 內存分析工具
6.1、MAT工具的介紹
想要深入的進行分析并確定內存泄漏,就要分析疑似發生內存泄漏時所生成堆存儲文件(hprof)。堆存儲文件可以使用DDMS或者Memory Monitor來生成,輸出的文件格式為hpof,而MAT就是來分析堆存儲文件的。
MAT(Memory Analyzer Tool)工具是一款功能強大的]ava堆內存分析器??梢杂糜诓檎覂却嫘孤┮约安榭磧却嫦那闆r。MAT是基于Eclipse開發的,不僅可以單獨使用,還可以作為插件的形式嵌入在Eclipse中使用。
下載地址 : https://www.eclipse.org/mat/downloads.php(當使用 MAT 工具報錯 jdk 版本過低時,此時可以參考:https://blog.csdn.net/health7788/article/details/123893540 指定該工具使用其他版本的 jdk。)
(注意,默認情況下,MAT 工具只能識別Oracle JDK的 dump 格式日志,而對于IBM JDK的.phd格式的日志,需要額外安裝插件DTFJ)
6.2、MAT工具的基本使用
用mat打開hprof文件后會看到的是一個餅狀圖,它主要用來顯示內存的消耗,餅狀圖的彩色區域代表被分配的內存,灰色區域的則是空閑內存,點擊每個彩色區域可以看到這塊區域的詳細信息,

6.3、MAT工具的視圖
MAT 幾個比較重要的視圖:Leak Suspects(泄露疑點)、histogram(直方圖視圖,列出每個類的實例數)、dominator tree(支配樹視圖,直接列出最大的類)
6.3.1、Leak Suspects(泄露疑點,可查看內存泄露的線程以及詳細堆棧信息)
打開懷疑泄露報告可能會看到有多個懷疑對象(Problem Suspect),可以從占用內存從大到小開始分析(報告里面占用內存都是用的字節單位,1,000,000,000 bytes = 1GB)
Leak Suspects 有的時候甚至連具體代碼的位置都幫我們定位好了,排查非常方便。

在有些比較簡單的問題里直接查看堆棧即可看到內存溢出原因,甚至可定位到具體代碼行,如下圖。當實際情況往往不會這么容易就能排查出問題,需要結合樹狀圖等其他視圖內容查看。

6.3.2、dominator tree(樹狀圖,可排序查看占用內存大的類)
- Retained Heap:類所占用的內存大小,以字節為單位
- Percentage:表示占用 JVM 內存的百分比
下圖中可看出有一個占用 99.42% 內存的對象,里面包含了一個字符串對象,該對象即是導致內存溢出的問題。

有時可能不是一個特別大的對象占滿了內存,而是有很多個小對象占滿內存,所以也要看下有沒有雖然排序在后面但是有很多個重復的相同對象的情況。
6.3.3、直方圖視圖(histogram)
查看對象占用的內存大小


可查看大對象堆內存占用情況的一個列表,列說明如下:
- Shallow Heap:大對象占用的堆內存大小
- Retained Heap:與這個大對象關聯的堆內存大小
7、IBM Heap Analyzer

浙公網安備 33010602011771號