说实话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的运行时数据,说实话个人看了好几遍感觉好深奥
由此可见堆外内存追踪技术手段也确实不少,基本上可以解决你遇到的大多数情况。
引用资料