一、 準備測試應用
- 新建一個 SpringBoot應用,寫一段有 OOM bug 的代碼:
@RestController
@RequestMapping
public class JvmThreadController {List<TestWrapper> memoryList = new ArrayList<>();@GetMapping("/test")public String memoryTest(@RequestParam("count") Integer count) {for (int i = 0; i < count; i++) {byte[] b = new byte[1024];memoryList.add(new TestWrapper(b));}return "success";}static class TestWrapper{byte[] b;public TestWrapper(byte[] b) {this.b = b;}}
}
-
調整JVM參數,可以快速 OOM
-Xmx50m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/java/
-
調用memoryTest接口,項目 OOM
二、使用Arthas排查問題
-
啟動 Arthas:
java -jar arthas-boot.jar
-
查看內存使用情況
dashboard
-
使用Arthas生成火焰圖
-
開始生成:
profiler start --event alloc
-
查看采集的sample的數量:
profiler getSamples
-
停止采集,并生成html文件,可以指定文件存放地址:`profiler stop --format html --file /java/output.html
- 分析火焰圖
根據火焰圖可以看出問題,是com/arthas/demo/controller/JvmThreadController.memoryTest
方法有問題,并且和byte[]
有關
三、另一種排查方式
-
使用
jps
命令查看項目進程
-
使用
jmap -histo:live 37446 | grep demo
命令查詢堆內存中的對象分配統計信息,通過jmap命令配合-histo:live選項獲取的,它展示了指定Java進程(PID為37446)的存活對象直方圖。
命令解釋:
- jmap: 是一個JDK自帶的命令行工具,用于打印堆內存的詳細信息,包括堆內存使用情況、dump堆內存到文件等。
- histo:live: 參數指定只統計活動對象(即未被垃圾回收器標記為可回收的對象)的信息。
- 37446:PID
- grep:執行搜索命令。
- demo:是要搜索的模式或字符串。在這里,我希望查找那些輸出行中包含 “demo” 字符串的行。
輸出解讀:
- 第一列:19, 這是一個索引編號,表示列表中的第19個條目。
- 第二列:4200, 實例數量,即堆中有4200個com.arthas.demo.controller.JvmThreadController$TestWrapper類型的對象。
- 第三列:67200, 占用內存大小,單位是字節,表明這4200個TestWrapper實例總共占用了67,200字節的堆空間。
jmap -histo:live 37446 | grep demo
命令默認是按照實例數量倒序排序的,但是有可能會出現大量數據,所以我們可以把命令優化成按照實例數量倒序排序,并且只取前 15 條數據:
jmap -histo:live 7 | awk '/demo/ {print $1, $2, $3, $4}' | sort -k2,2rn | head -n 15