最近开发同学反馈,某定时任务服务疑似有内存泄漏,整个进程的内存占用比 Xmx 内存大不少,而且看起来是缓慢上升的,做了下面这次分析,包括下面的内容:
- 分析 JVM native 内存的一些常见思路
- 内存增长了,怎么甄别是不是内存泄漏
- 一个完全不熟悉的项目如何找到可能导致 native 内存分配的代码
- 经典的 Linux 64M 内存问题
- 到底是内存碎片还是内存泄漏
现象
这个定时任务的应用设置 Xmx 为 925M,但是 native 内存缓存持续增长,但是增长到一定阶段也会保持稳定,不再继续增长。
是内存泄漏吗?
不管是不是内存泄漏,首先要搞清楚的是这段增长的内存是什么,土方法就是用 pmap -x
持续观察内存地址空间的变化。
经过几个小时的 pmap 后台运行,很快发现堆内存几乎无变化,增长的区域都在 64M 内存空间,这就是经典的 glibc 内存分配 64M 问题。
关于 Linux 64M 内存问题,我之前写过几篇相关的文章,大家感兴趣可以去看。
从这里基本可以确定是 native 带来的问题,接下来就是 dump 出来看里面到底存了什么。这里有几个方法
- 使用 gdb
- 写一个脚本读取
/proc/<pid>/mem
- 我自己用 Go 写的一个小工具(可能过段时间释放出来)
脚本内容如下:
cat /proc/$1/maps | grep -Fv ".so" | grep " 0 " | awk '{print $1}' | grep $2 | ( IFS="-"
while read a b; do
dd if=/proc/$1/mem bs=$( getconf PAGESIZE ) iflag=skip_bytes,count_bytes \
skip=$(( 0x$a )) count=$(( 0x$b - 0x$a )) of="$1_mem_$a.bin"
done )
执行这个脚本,传入进程号和起始地址就可以把对应内存 dump 到文件中。接下来可以通过 strings 初步查看文件里面有没有认识的字符串。通过 strings 发现很多 jar 包文件里的内容,部分内容如下:
这个内容是项目依赖 jar 包 HikariCP-2.5.1.jar 的 MANIFEST.MF
文件的内容
.
├── M