本文将模拟一个内存溢出环境,重现生产服务器排查过程。
## 一、环境搭建
1. 使用SpringBoot应用进行模拟,代码如下:
```
...
@RequestMapping("/oom/creation")
public void createOOM() {
List
for (;;){
oomList.add(new OOMObject());
}
}
```
2. 接下来,我们在服务器上进行部署,为避免影响主机上其他服务,这里我们设置最大堆内存为**128MB**:
`nohup java -jar -Dserver.port=8083 -Xms128m -Xmx128m springboot-web-demo-1.0-SNAPSHOT.jar &`
注意这里需要已经安装`JDK`,如果未安装,请参考:[Linux安装OpenJDK](https://www.jianshu.com/p/f1e549d533da).
3.最后我们检查下应用启动状态,执行:
`jps`

或 `ps -ef | grep java | grep -v grep`

可以看到启动了一个进程号为**16327**的应用。
## 二、模拟内存溢出
模拟前,我们先通过`top -Hp 16327(pid)`看下该进程CPU使用情况:
```
-H 线程模式
-p 指定进程ID
```

然后调用之前定义好的接口,进行模拟:

也可以直接通过**浏览器访问**或者使用**curl命令调用**。调用之后再使用`top -Hp 16327(pid)`查询进程CPU使用情况如下:

这里我们看到内存占用在几秒钟就飙升到82.5%。
## 三、问题排查
接下来我们通过`jstack`查看PID的16330的线程,这里我们先把PID转为16进制:
`printf "x%\n" 16330`

再通过`jstack`命令查看该线程:
`jstack -l 16327 | grep -20 3fca`

我们发现该线程为GC线程,接下来通过`jmap`查询GC情况,我们这里直接看堆内存的对象情况:
`jmap -histo 16327 | head -n 10`

可以看到OOMObejct对象创建了400多万个实例,明显异常,我们通过搜索代码OOMObject对象的usages,发现该代码在:
```
...
@RequestMapping("/oom/creation")
public void createOOM() {
List
for (;;){
oomList.add(new OOMObject());
}
}
```
这里有一个死循环,至此问题排查到,修改后重新上线。
## 四、其他排查方式
因为线上服务器安全问题,我们这里使用的是JDK、Linux提供的原生命令进行排查,其他方式参考:
- 通过**JConsole**、**jvisualvm**分析dump日志
- 通过**arthas**排查
这里我们演示下arthas:
1.启动arthas
```
wget https://alibaba.github.io/arthas/arthas-boot.jar
java -jar arthas-boot.jar
```
2.我们这里简单演示下dashboard命令:

可以看到GC线程的内存率较高。其他方式具体请**查看参考资料**。
## 五、参考资料
1. [arthas](http://arthas.gitee.io/)
2. [JConsole](https://www.apiref.com/java11-zh/jdk.jconsole/module-summary.html)
3. [JVisualVM](https://cloud.tencent.com/developer/article/1833559)
4. [top](http://events.jianshu.io/p/d58638e765f4)
5. [jmap](https://www.lifengdi.com/archives/article/2082)