
通过 JVM NMT(native memory tracking)来追踪分析堆外内存
只能 Track JVM 自身的内存分配
第三方的 Native 库内存无法 track,不能 Trace JNI 里直接调用 malloc 时的内存分配,典型场景如 ZipInputStream 场景

使用NMT导出内存
jcmd 1 VM.native_memory detail scale=MB > nmt-`date +%F-%H-%M-%S`.log通过多次导出比较,分析内存的变化。
如果内存没有变化,那需要怀疑是否是堆外的内存变化了
堆外内存使用java工具已经不能胜任。
需要更底层的一些内存分析工具
pmap -x 1 > pmap-`date +%F-%H-%M-%S`.log
两次的内存区别
icdiff pmap-2023-07-27-09-46-36.log pmap-2023-07-28-09-29-55.log | less -SR

查看内存中的具体内容
tail -c +$((0x00007fa9bc000000+1)) /proc/1/mem|head -c $((11616*1024))|strings|less -S根据pmap-2023-07-27-09-46-36.log 看下RSS占用大小
# 只统计可写权限的内存
awk '/^[0-9a-f].*rw/ { sum += $3 } END { printf "可写内存RSS: %.2f MB\n", sum/1024 }' 1.txt
awk '{ sum += $3 } END { printf "%.2f MB\n", sum/1024 }' 1.txtstrace
向 os 追踪申请内存请求(系统调用),常见用法:
strace -f -e "brk,mmap,munmap" -p pid
1、使用pmap -x {pid} > ./pmap.txt 获取内存映射信息到文件里。
2、使用jcmd ${pid} VM.native_memory detail > nmt.txt获取NMT统计的JVM内存详细信息。
3、找nmt中没有但是在pmap中有的内存块地址(tips1:最好找64MB的内存块)
4、使用上面的dump.sh:./dump.sh {pid} {addr} dump对应内存块的内容到磁盘上。
5、使用 strings {dump文件名} > dumped.txt转换为可读的文本,然后根据文本内容进一步寻找问题的原因
上面的5个步骤可以直接使用 https://github.com/zhuxingsheng/awesome-java-tools 中的 memleak.sh
./memleak.sh show pid
列出可能存在泄漏的内存映射地址:
00007f2824000000 19396 19396 19396 rw--- [ anon ]
00007f28252f1000 46140 0 0 ----- [ anon ]
00007f2830000000 9752 9672 9672 rw--- [ anon ]
00007f2830986000 55784 0 0 ----- [ anon ]
00007f2834000000 11624 11624 11624 rw--- [ anon ]
命令行执行:
./memleak.sh dump pid addr
得到内存块里的内容进一步分析,输出如下:
Dump文件已输出至: ./11983_mem_7f2964000000.bin最终会产出一个泄漏内存块的文件
这个文件中包含的就是该地址块的内容,但是是二进制的,可以使用如下命令转为字符串方便查看:
strings 11983_mem_7f2964000000.bin > 11983_mem_7f2964000000.bin.txt
记一次堆外内存泄漏分析[1]
https://juejin.cn/post/7338388292969283584
https://www.cnblogs.com/codelogs/p/17659370.html
Java 进程内存占用及可观测性调研&内存异常排查最佳实践[2]
[1] 记一次堆外内存泄漏分析: https://blog.csdn.net/zhuqiuhui/article/details/128513480
[2] Java 进程内存占用及可观测性调研&内存异常排查最佳实践: https://www.pengzna.top/article/Java-Memory/