堆外內存泄露揭祕

說實話jvm中堆外內存使用的場景非常多,它可降低GC、減少用戶態與內核態切換及數據拷貝,已經成爲性能提升的有效手段。比如通訊(netty之對象池Recycler),MQ(本地IO之零拷貝之類)。一般使用堆外內存要重點關注手動釋放or 自動釋放,從筆者的過往經驗中談,堆外內存一旦發生泄漏,排查起來相對的困難。

你可能比較好奇,爲何比較困難呢?筆者認爲:涉及泄漏最終會定位到c的代碼,一般都是內核級gcc之類(各種.so的包,比如:zjbmalloc.so)就無法再繼續定位追蹤了,再深挖對java程序員而言的確是心有餘而力不足。

關於內存泄漏,其實筆者很早以前就分享過,這裏歸納總結下:

1. JVM故障診斷調優

2. Netty的資源泄露探測器:ResourceLeakDetector

3. 下面列舉一些有用工具

查看內存分佈

項目中添加JVM參數-XX:NativeMemoryTracking=detail然後重啓項目,使用命令jcmd pid VM.native_memory detail查看到的內存分佈,jcmd命令顯示的內存包含堆內內存、Code區域、通過unsafe.allocateMemory和DirectByteBuffer申請的內存,但是不包含其他Native Code(C代碼)申請的堆外內存。

查看內存分佈

通過linux命令pmap,通過這些地址空間判斷是否在jcmd命令中,如果不在可以確定是Native Code所引起。

gperftools + strace + GDB + jstack

gperftools原理就使用動態鏈接的方式替換了操作系統默認的內存分配器(glibc),可以用個觀察Native Code內存使用情況

項目啓動時使用strace -f -e"brk,mmap,munmap" -p pid追蹤向OS申請內存請求,找到可疑內存申請

gdp -pid pid進入GDB之後,然後使用命令dump memory mem.bin startAddress endAddressdump內存,其中startAddress和endAddress可以從/proc/pid/smaps中查找。然後使用strings mem.bin查看dump的內容

使用命令jstack pid去查看線程棧,找到對應的線程棧(注意10進制和16進制轉換)

JVM界頂級大佬R的文章

借HSDB來探索HotSpot VM的運行時數據,說實話個人看了好幾遍感覺好深奧

由此可見堆外內存追蹤技術手段也確實不少,基本上可以解決你遇到的大多數情況。

引用資料

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章