內存泄露排查記錄
一 、問題定位
手段一:通過 jstat -gcutil 快速定位GC問題(首先)
命令格式: jstat -gcutil <pid> <period>
命令樣例:jstat -gcutil 11900 3s
------> 監控進程11900的GC情況, 每3s輸出一條記錄。
要點:O列(老年代內存使用率)一直接近100%;
FG列(Full GC次數) 一直增長。
手段二:增加Xmx參數,加大堆內存排除堆內存過小因素影響。
在jstat -gcutil 的O列占滿情況,需進一步排除是否是堆內存分配過小,滿足不了業務請求量導致。
查看最大堆內存命令:jinfo -flag MaxHeapSize <pid> 。
假如開始是4G,修改成8G:java -Xmx8G
要點:增加最大堆內存後,使用jstat -gcutil 看O列是否滿負,如果還是滿負, 則可基本確定是內存泄露。
手段三:使用 jmap -histo 命令定位內存泄露具體對象。
該命令展示所有類的實例個數以及內存用量情況,並按照使用量降序輸出,一般自己寫的類有內存泄露,那會名列前茅。
命令1:jmap -histo <pid>
命令樣例:jmap -histo 42530 | head -n 20
輸出對應進程當前所有存活對象的堆內存占用情況,找到"突出" 的嫌疑類。
命令2:jmap -histo:live <pid>
帶上live,會觸發一次Full GC後再輸出結果,實際上就是回收了無用的對象,輸出真正存活的對象情況。如果嫌疑類名實例數不減,那麽十有八九這個類就是導致內存泄露根因。
要點:兩個命令對比觀察Full GC後,實例數不減的類,重點關註排在前面的幾個自己寫的類名。
手段四:使用 jmap -dump:file 導出堆內存數據。
通過手段三,一般可以定位出代碼位置,但代碼中很多地方引用了這個類,則要導出 Dump 文件,進一步分析泄露對象的GCRoot 。
命令:jmap -dump:file=<文件名> <pid>
樣例:jmap -dump:file=app.dump 9336
分析dump文件的工具:除了JDK自帶的 jhat , jvisualvm ,還有第三方的MAT,jprofiler等。
內存泄露的話,dump文件也會很大,為了防止卡死,一般會將Xmx控制在一個合適的大小(2G) ,重現問題後, 再導出dump 。
要點:將Xmx設小一點,再導出dump。
手段五:jhat分析dump文件,尋找GC Root。
因為網絡拷貝大容量的dump文件諸多不便,所以需要現網直接分析dump文件,jhat可以啟動一個http服務,提供頁面遠程分析對象引用情況。
命令:jhat -J-Xmx2G -port <port> <dump文件>
樣例:jhat -J-Xmx2G app.dump
其中Xmx2G代表使用2G堆內存運行jhat,下一步可瀏覽器分析了,URL:http://ip:7000
要點:首先鏈接到嫌疑類的具體某個對象頁面,分析這個對象的引用情況是否正常,再通過“Reference chains from Rootset ”獲取所有的GC Root ,進而定位到具體代碼位置。
內存泄露排查記錄