說實話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 endAddress
dump內存,其中startAddress和endAddress可以從/proc/pid/smaps中查找。然後使用strings mem.bin
查看dump的內容使用命令jstack pid去查看線程棧,找到對應的線程棧(注意10進制和16進制轉換)
JVM界頂級大佬R的文章
借HSDB來探索HotSpot VM的運行時數據,說實話個人看了好幾遍感覺好深奧
由此可見堆外內存追蹤技術手段也確實不少,基本上可以解決你遇到的大多數情況。
引用資料