用於分析離線 Java 堆轉儲的工具
1. 概述
分析記憶體消耗對於提升Java應用程式效能和避免記憶體問題至關重要。由於Java應用程式在JVM堆中分配對象,因此監控堆記憶體並分析其託管的對象非常重要。
在本教程中,我們將介紹兩種不同的記憶體分析器,以示範如何透過離線堆轉儲檔案分析 Java 堆記憶體。
2. 捕獲堆轉儲
堆轉儲檔案是特定時間點堆中所有物件的快照。我們可以使用諸如 VisualVM 之類的工具來建立堆轉儲檔案。
VisualVM 是一款免費的 Java 分析工具,在 JDK 8 及更高版本中與 JDK 捆綁在一起。在 JDK 8 之後,它作為獨立應用程式分發。
我們需要一個程序,讓 VisualVM 能夠擷取堆轉儲。讓我們建立一個範例程式來故意造成記憶體洩漏:
public class MemoryLeakDemo {
private static final List<InputStream> leakList = new ArrayList<>();
public static void main(String[] args) throws InterruptedException {
int counter = 0;
while (true) {
byte[] chunk = new byte[1024 * 1024];
ByteArrayInputStream bais = new ByteArrayInputStream(chunk);
leakList.add(bais);
counter++;
System.out.println("Allocated " + counter + " MB");
Thread.sleep(100);
}
}
}
程式包含一個名為leakyList.這意味著它永遠不會被垃圾回收。主方法中有一個循環,它會不斷地向這個靜態列表中寫入 1MB 的內存,這將導致內存洩漏。
一切準備就緒,我們可以執行 VisualVM 來進行效能分析。啟動 VisualVM 後,我們可以在左側的「應用程式」標籤中看到正在執行的 Java 進程清單:
現在,我們可以運行範例程序,並看到相應的 Java 進程顯示出來。選取該進程,然後切換到右側面板的「監控」標籤。這裡會顯示堆記憶體消耗圖表。點選「堆轉儲」按鈕產生堆轉儲快照。
我們可以將該快照匯出為 .hprof 檔案以進行離線分析。 .hprof 檔案為二進位格式,需要分析器才能讀取。
3. VisualVM
VisualVM 也具備匯出堆轉儲檔案以供離線分析的功能。
開啟堆轉儲檔案後,VisualVM 會顯示堆疊轉儲的摘要,其中包含 JVM 版本等常規資訊以及環境變數清單(位於「系統屬性」下):
其中最有價值的資訊部分是「按實例數量排列的類別」和「按實例大小排列的類別」。前者顯示了創建實例數量最多的前 5 個類,後者顯示了佔用堆記憶體最多的前 5 個類。
如果我們把視圖從“摘要”切換到“物件”,就可以看到駐留在堆記憶體中的物件。我們將看到物件創建的來源。在下面的範例中,我們可以看到位元組數組是在MemoryLeakDemo中創建的:
4. Eclipse 記憶體分析工具 (MAT)
儘管 VisualVM 可以執行基本的堆疊檢查,但 Eclipse MAT 能夠產生更全面的記憶體分析報告。
將堆轉儲檔案匯入 Eclipse MAT 後,我們可以透過「概覽」標籤讀取多個報告,例如「主要資源消耗者」和「主要元件」:
檢測記憶體洩漏最有幫助的工具是洩漏嫌疑犯報告。它會產生一份分析報告,找出堆轉儲中所有潛在的記憶體洩漏:
透過閱讀報告詳情,我們可以追蹤物件的建立位置,這與我們在 VisualVM 中看到的情況類似:
除了這些綜合報告外,Eclipse MAT 還支援物件查詢語言 (OQL),這是一種類似 SQL 的語言,用於查詢堆轉儲。
讓我們發出一個範例查詢,選擇所有大小至少為 1MB 的位元組數組:
SELECT *
FROM byte[] obj
WHERE (obj.@length >= 1048576)
查詢結果顯示共有 715 個位元組數組,每個數組的大小至少為 1MB。這一數量與洩漏嫌疑人報告中的發現相符:
6. 結論
記憶體分析是優化 Java 應用程式效能的關鍵。本文探討如何捕捉堆轉儲並將其匯出為離線檔案。
我們還研究了各種記憶體分析器,例如 VisualVM 和 Eclipse MAT,以識別記憶體洩漏。