现象

  1. 服务器运行一段时间后,内存占用越来越高,最终导致服务器OOM。
  2. 通过查看相关的快照和监控,发现堆内存使用正常,但是机器的RSS一直在增长。

img_1.png
img_4.png

预排查

  1. 是否创建了大对象
  2. 是否使用了堆外内存,且未及时释放内存
  3. 是使用了jni方法,且未及时释放内存
  4. 是否使用了相关资源, 比如stream, file, socket等,且未及时释放资源

定位

  1. 首先使用jmap把内存快照dump下来

img_3.png
img.png

如果是堆内内存泄露的话, 通过快照分析基本就能定位到问题,但是堆外内存泄露的话,就需要通过其他方式定位了

  1. 同时通过确认线程状态和数量,确认也不是线程数量导致的内存泄露
  2. 因此需要排查是否是堆外内存泄露导致的
0. 开启NMT

由于是线上环境,暂不考虑开启NMT,因此需要通过其他方式定位

1. 使用jmap命令

img_5.png

发现MaxMetaspaceSize未设置最大值,那么是否可能是元空间内存泄露导致的呢?

2. 观察类加载和卸载情况

在本地环境,添加启动参数-verbose:class,观察类加载和卸载情况
再本地环境跑一段时间,然后手动出发gc, 通过 jmap 命令或者 System.gc()
发现类加载也正常, 没有什么特别的情况

3. 使用strace和pmap命令

参考https://ww.dandelioncloud.cn/article/details/1599739989472788481

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
pmap -x PID
````

![img_2.png](img_2.png)

发现确实有大内存的使用

使用 strace 命令追踪系统调用

```shell
## 追踪PID进程的brk,mmap,munmap系统调用
## 查看那些线程申请了较大内存, 然后在jstack查看线程栈信息,注意线程号 10进制和16进制的转换
strace -f -e "brk,mmap,munmap" -p PID
## 输出线程栈信息, 查看那个线程申请了大内存,然后具体看代码排查
jstack -l PID > file.dump
3. 使用gperftools
linux
1
2
3
4
5
6
7
8
9
10
## 使用 yum 安装
yum install gperftools libunwind
## 可选,PDF 支持
yum install graphviz
## 设置环境变量, 可在idea启动设置中添加,地址应该不一样,具体看安装路径
export LD_PRELOAD=/usr/lib64/libtcmalloc.so
export HEAPPROFILE=/DATA1/admin_tmp/gzip
## 启动 Java 程序
## 分析内存占用,文本显示结果
pprof --text /usr/bin/java /DATA1/admin_tmp/gzip.0001.heap
mac
1
2
3
4
5
6
7
8
9
10
## 使用 Brew 安装
brew install gperftools 或者 brew install --build-from-source gperftools
## 可选,PDF 支持
brew install graphviz 或者 brew install --build-from-source graphviz
## 设置环境变量, 可在idea启动设置中添加,地址应该不一样,具体看安装路径
export DYLD_INSERT_LIBRARIES=/usr/local/Cellar/gperftools/2.8.1/lib/libtcmalloc.dylib
export HEAPPROFILE=/Users/cheng/tmp/test.log
## 启动 Java 程序
## 分析内存占用,文本显示结果
pprof --text /Library/Java/JavaVirtualMachines/jdk-11.0.10.jdk/Contents/Home/bin/java ~/tmp *.heap