内存溢出(OOM)问题排查

本文将模拟一个内存溢出环境,重现生产服务器排查过程。
## 一、环境搭建
1. 使用SpringBoot应用进行模拟,代码如下:

```
...
@RequestMapping("/oom/creation")
public void createOOM() {
    List<OOMObject> oomList = new ArrayList<>();
    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`

![b0a7d18eb4378d7047513f005543d53.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/00571671323a43a28d2d8d5592814d92~tplv-k3u1fbpfcp-watermark.image?)

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

![3ca45656ed157204874da4d03b5ff1d.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a21669deb0b2445aa4729788ee57e7b5~tplv-k3u1fbpfcp-watermark.image?)

可以看到启动了一个进程号为**16327**的应用。
## 二、模拟内存溢出
模拟前,我们先通过`top -Hp 16327(pid)`看下该进程CPU使用情况:
```
-H 线程模式
-p 指定进程ID
```
![e996316f413a43e9478b4e2ef795991.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/7539c08342314648b2fb85e8aff0a7f5~tplv-k3u1fbpfcp-watermark.image?)

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

![1e3c4252a4e2ff12154da7d3fa2b305.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/bd71b4c508184bcd90fac6200ba42428~tplv-k3u1fbpfcp-watermark.image?)
也可以直接通过**浏览器访问**或者使用**curl命令调用**。调用之后再使用`top -Hp 16327(pid)`查询进程CPU使用情况如下:

![8d062f2f1162c76b182f6041dc63496.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/bb25e3e8c7b64d808f508fc91bc01c5f~tplv-k3u1fbpfcp-watermark.image?)
这里我们看到内存占用在几秒钟就飙升到82.5%。

## 三、问题排查
接下来我们通过`jstack`查看PID的16330的线程,这里我们先把PID转为16进制:

`printf "x%\n" 16330`

![100f4e3a4c0cb2895ee6880b536fcaa.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/33399ad4cf1640e5ab6dd0c84543e955~tplv-k3u1fbpfcp-watermark.image?)

再通过`jstack`命令查看该线程:

`jstack -l 16327 | grep -20 3fca`


![39453fdaa1448f4e74695f4640b803d.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c04850dabcc442bbb15fa7fbce2a45a2~tplv-k3u1fbpfcp-watermark.image?)

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

`jmap -histo 16327 | head -n 10`

![c366b584cd5fe9a159c5723157c3690.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ce382dea221846c9a5ec92901487a5ae~tplv-k3u1fbpfcp-watermark.image?)
可以看到OOMObejct对象创建了400多万个实例,明显异常,我们通过搜索代码OOMObject对象的usages,发现该代码在:
```
...
@RequestMapping("/oom/creation")
public void createOOM() {
    List<OOMObject> oomList = new ArrayList<>();
    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命令:


![e395857be47a44737d9274261650af6.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5e6cf6929b334d29a01f5d10b3652490~tplv-k3u1fbpfcp-watermark.image?)

可以看到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)

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值