堆外内存泄露揭秘

说实话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的运行时数据,说实话个人看了好几遍感觉好深奥

由此可见堆外内存追踪技术手段也确实不少,基本上可以解决你遇到的大多数情况。

引用资料

  • 记一次ClassLoader死锁
  • Spring Boot内存泄露排查记

你可能感兴趣的:(Java)