一、前言
在本文中,我們總結下抓 Java dump 的幾種不同方法。
Java Heap Dump 是特定時刻 JVM 內存中所有對象的快照。它們對於解決內存泄漏問題和分析 Java 應用程序中的內存使用情況非常有用。
Java Heap Dump 通常以二進制格式的 hprof 文件存儲。我們可以使用 jhat 或 JVisualVM 之類的工具打開和分析這些文件。同樣,使用 MAT 工具分析是很常見的。
二、JDK 工具包
JDK 附帶了幾個以不同方式 Heap Dump 的工具。所有這些工具都位於 JDK 主目錄下的 bin 文件夾下。因此,只要這個目錄包含在系統路徑中,我們就可以直接從命令行啟動它們。
1、jmap
jmap 是一種工具,用於打印有關正在運行的 JVM 中的內存的統計信息。我們可以將其用於本地或遠端進程。
要使用 Jmap Heap Dump ,我們需要使用 Heap Dump 參數:
jmap -dump:[live],format=b,file=<file-path> <pid>
與該參數一起,我們應該指定幾個參數:
- live:如果設置,則僅打印具有活動引用的對象,並丟棄准備好進行垃圾回收的對象。此參數是可選的。
format = b
:指定轉儲文件將采用二進制格式。如果未設置,結果是相同的- file:將寫入的文件
- pid:Java 進程的 ID
舉一個例子是這樣的:
jmap -dump:live,format=b,file=/tmp/dump.hprof 12587
注意:我們可以使用 jps 命令輕松獲取 Java 進程的pid。jmap 是作為實驗工具在 JDK 中引入的。因此,在某些不支持的情況下,最好使用其他工具代替。
2、jcmd
jcmd 是一個非常完整的工具,可以通過向 JVM 發送命令請求來工作。我們必須在運行 Java 進程的同一台機器上使用它。
它的最多命令就是 GC.heap_dump
,我們可以通過指定進程的 pid 和輸出文件路徑來使用它來 Heap Dump:
jcmd <pid> GC.heap_dump <file-path>
我們可以使用上面例子中使用的相同參數執行它:
jcmd 12587 GC.heap_dump /tmp/dump.hprof
與 jmap 一樣,生成的 dump 為二進制格式。
3、JVisualVM
JVisualVM 是帶有圖形用戶界面的工具,它使我們可以監控 Java 應用程序,對其進行故障排除和分析。GUI 簡單,直觀並且易於使用。
我們可以右鍵單擊 Java 進程並選擇“線程dump”選項,該工具將創建 dump 並在新選項卡中將其打開:

注意:我們可以在“基本信息” 部分中找到創建的文件的路徑。從 JDK 9開始,Visual VM 不包括在 Oracle JDK 和 Open JDK 發行版中。因此,如果我們使用的是 Java 9 或更高版本,則可以從 Visual VM 開源項目站點獲得 JVisualVM 。地址:https://visualvm.github.io/
三、自動抓取 heap dump
上面介紹所有工具均在特定時間手動去 dump 的。在某些情況下,我們希望在發生 java.lang.OutOfMemoryError
時獲取 Heap Dump ,以幫助我們分析問題。
對於這種情況,Java 提供了 HeapDumpOnOutOfMemoryError
命令行參數,當拋出 java.lang.OutOfMemoryError
時,程序會生成 heap dump :
java -XX:+HeapDumpOnOutOfMemoryError
默認情況下,它將 dump 存儲在我們正在運行應用程序的目錄中的 java_pid <pid> .hprof
文件中。如果要指定另一個文件或目錄,可以在 HeapDumpPath
參數中進行設置:
java -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=<file-or-dir-path>
使用此參數,當我們的應用程序內存不足時,我們將能夠在日志中看到包含 dump 的已創建文件:
java.lang.OutOfMemoryError: Requested array size exceeds VM limit
Dumping heap to java_pid12587.hprof ...
Exception in thread "main" Heap dump file created [4744371 bytes in 0.029 secs]
java.lang.OutOfMemoryError: Requested array size exceeds VM limit
at com.baeldung.heapdump.App.main(App.java:7)
在上面的示例中,它已寫入到 java_pid12587.hprof
文件中。
就像我們看到的,此參數非常有用,使用此參數運行應用程序時沒有任何開銷。因此,強烈建議始終使用此參數,尤其是在生產中。
最后,還可以在運行時通過使用 HotSpotDiagnostic MBean
來指定此參數。為此,我們可以使用 JConsole 工具並將HeapDumpOnOutOfMemoryError
VM 參數值設置為 true:
四、JMX
我們將使用在上一節中簡要介紹的 HotSpotDiagnostic MBean
。該 MBean 提供了一個 dumpHeap 方法,該方法接受 2 個參數:
- outputFile:dump 文件的路徑。該文件應具有 hprof 擴展名
- live:如果設置為 true,則它僅 dump 內存中的活動對象,就像我們之前在 jmap上看到的那樣
下面我們將介紹兩種不同的方法來調用此方法來 heap dump。
1、JConsole
使用 HotSpotDiagnostic MBean
的最簡單方法是使用 JMX 客戶端(例如JConsole)
打開 JConsole 並連接到正在運行的 Java 進程,則可以導航到 MBeans 選項卡並在 com.sun.management
下找到HotSpotDiagnostic。在操作中,我們可以找到我們之前描述的 dumpHeap 方法:
如所示,為了執行 dumpHeap 操作,我們只需要引入參數 outputFile 並將其存在於 p0 和 p1 文本字段中。
2、編程
使用 HotSpotDiagnostic MBean
的另一種方法是通過從 Java 編程方式調用它。
為此,我們首先需要獲取一個 MBeanServer 實例,以便獲取在應用程序中注冊的 MBean。之后,我們只需要獲取 HotSpotDiagnosticMXBean 的實例 並調用其 dumpHeap 方法。
import javax.management.MBeanServer;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.nio.file.Paths;
public class HeapDump {
public static void dumpHeap(String filePath, boolean live) throws IOException {
MBeanServer server = ManagementFactory.getPlatformMBeanServer();
HotSpotDiagnosticMXBean mxBean = ManagementFactory.newPlatformMXBeanProxy(
server, "com.sun.management:type=HotSpotDiagnostic", HotSpotDiagnosticMXBean.class);
mxBean.dumpHeap(filePath, live);
}
public static void main(String[] args) throws IOException {
String file = Paths.get("dump.hprof").toFile().getPath();
dumpHeap(file, true);
}
}
注意,不能覆蓋 hprof 文件。因此,在創建打印 heap dumps 的應用程序時應該考慮到這一點。如果我們沒有這樣做,就會得到一個異常:
Exception in thread "main" java.io.IOException: File exists
at sun.management.HotSpotDiagnostic.dumpHeap0(Native Method)
at sun.management.HotSpotDiagnostic.dumpHeap(HotSpotDiagnostic.java:60)
五、Arthas
Arthas 是阿里提供的一款 Java 開源診斷工具。能夠查看應用的線程狀態、JVM 信息等;並能夠在線對業務問題診斷,比如查看方法調用的出入參數、執行過程、拋出的異常、輸出方法執行耗時等,大大提升了線上問題的排查效率。
Arthas 提供 heapdump 命令:dump java heap, 類似 jmap命令的 heap dump功能。
dump 到指定文件:
[arthas@58205]$ heapdump /tmp/dump.hprof
Dumping heap to /tmp/dump.hprof...
Heap dump file created
只 dump live對象:
[arthas@58205]$ heapdump --live /tmp/dump.hprof
Dumping heap to /tmp/dump.hprof...
Heap dump file created
dump 到臨時文件:
[arthas@58205]$ heapdump
Dumping heap to /var/folders/my/wy7c9w9j5732xbkcyt1mb4g40000gp/T/heapdump2019-09-03-16-385121018449645518991.hprof...
Heap dump file created
更多參考:https://arthas.gitee.io/heapdump.html
六、結論
在本文中,我們展示了用 Java 捕獲 Heap Dump 方法 的多種方法。
常見的dump方式
從經驗上來說,我們應該記得在運行 Java 應用程序時始終使用 HeapDumpOnOutOfMemoryError 參數。