---
order: 5
---
# OOM（OutOfMemoryError）故障排查

\[\[toc]]

当系统启动后一起正常，但是使用一段时间后或者用户量多了之后就卡了，一直没响应，CPU使用率很高，重启服务后又正常了，这时很可能就是OOM了。

## OutOfMemoryError原因{#what-is-oom}

:::tip
本章节中涉及到操作JVM启动参数的操作请参考[Tomcat设置启动环境变量](../install/middleware/tomcat.md#env)
:::

### 1.java.lang.OutOfMemoryError: Java heap space{#oom-heap}

堆内存不够用了，堆内存32位系统最好大于1G，64位最好大于2G，但不要太大，太大会导致gc慢，更不要大于或接近物理内存大小，否则会使用交换空间虚拟内存严重降低系统性能。

**解决方法**

建议增大JVM启动参数`-Xmx=2048m`

### 2.java.lang.OutOfMemoryError: PermGen space{#oom-permgen}

:::tip
这个异常只对JDK7及以下版本，JDK8已经去掉了PermGen space，改为Metaspace了。JDK8最好设置参数`-XX:MaxMetaspaceSize=256m`，避免JVM无节制的使用内存。
:::

PermGen space的全称是`Permanent Generation space`，是指内存的永久保存区域, 这块内存主要是被JVM存放Class和Meta信息的，Class在被Loader时就会被放到PermGen space中, 它和存放类实例(Instance)的Heap区域不同，GC(Garbage Collection)不会在主程序运行期对 PermGen space进行清理，所以如果你的应用中有很多CLASS的话（包括Spring动态产生的类），就很可能出现PermGen space错误。

**解决方案**

建议增加JVM启动参数`-XX:MaxPermSize=256m`

MaxPermSize参数表示JVM可用的最大PermGen space，MaxPermSize参数大小为32M，MaxPermSize不会占用Max Heap Size（-Xmx）中设置的大小，如果设置：-Xmx1024m -XX:MaxPermSize=256m，那么Max Heap Size是1024m，不是1024m-256m

### 3.java.lang.OutOfMemoryError: GC overhead limit exceeded{#oom-overhead-limit-exceeded}

:::tip
java.lang.OutOfMemoryError: GC overhead limit exceeded，这个是JDK6新添的错误类型。是发生在GC占用大量时间为释放很小空间的时候发生的，是一种保护机制。
:::

JVM GC过程耗时很久但只回收了不到2%的可用内存，如果已经设置了Xmx参数，那么可能是由于某些用户操作导致系统长时间占用大量内存无法GC。在JSP导入大excel时，也会出现此错误提示。

**解决方案**

增加JVM启动参数`-XX:-UseGCOverheadLimit`，更多内容可参考网页[调优JVM内存](https://www.iteye.com/blog/java-boy-463454)

## OutOfMemoryError故障确诊{#oom-check}

当系统cpu消耗很高，而且线程都在做gc的时候可能就是有OOM了，如何确诊呢：

### 看看最忙的线程在干啥{#threads-check}

```sh
top //显示耗费cpu的进程
top -p 进程号 -H //显示这个进程内最好分cpu的线程，把列出的pid转换成小写16进制可在JVM堆栈中找到堆栈信息
```

![top](./images/2020-05-29-11-37-11.png)
![pid](./images/2020-05-29-11-40-40.png)

### 设置gc日志记录，并分析gc日志{#gc-logs-check}

给JVM设置gc日志记录：`-verbose:gc -Xloggc:garbage-collection.log`(参见[oracle官方说明](http://docs.oracle.com/javase/8/docs/technotes/tools/windows/java.html))，然后使用网站[gceasy](http://gceasy.io/index.jsp)分析日志：

![分析日志1](./images/2020-05-29-11-35-36.png)

![分析日志2](./images/2020-05-29-11-35-58.png)

当提示`Our analysis tells that your application is suffering from memory leak. It can cause OutOfMemoryError, JVM to freeze, poor response time and high CPU consumption.` 时就说明有内存漏洞了。

## OutOfMemoryError故障排查{#heapdump}

### 参数自动导出Heapdump{#heapdump-path}

如果已经进行了合理的`Xmx`和`XX:MaxPermSize`设置，还是出现**OutOfMemoryError**，那需要咨询研发工程师排查问题，为了帮助研发工程师解决问题，增加如下JVM参数以便在出现OOM的时候获得JVM的HeapDump文件：

```sh
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/path/to/store/dumpfiles
```

当下次出现OOM后JVM会产生heapdump文件在`/path/to/store/dumpfiles` 目录下，请自行修改`/path/to/store/dumpfiles`的具体位置，确保`/path/to/store/dumpfiles`是一个存在的目录，且启动JVM的系统用户有权限写这个目录，如果下次出现OOM，把HeapDump文件（很大，需压缩后）发送给研发工程师。

### jmap命令导出Heapdump{#heapdump-jmap}

如果系统日志已经发现OOM错误，而未配置`-XX:+HeapDumpOnOutOfMemoryError`参数，并且内存占用比较高，可以通过jdk自带的jmap命令导HeapDump。

```sh
./jmap -dump:live,format=b,file=heap.hprof <pid>
```

### 分析heapdump文件{#heapdump-analyse}

生产环境内存一般配置比较高比如10G左右，产生的heapdump文件比较大，可以使用现场测试环境服务器安装MAT，采用命令行进行分析，把分析的结果发给研发进行排除问题。

**MAT下载**

点击[此处](https://www.eclipse.org/mat/downloads.php)下载MAT，选择Linux(X86\_64/GTK+).

**配置MAT**

解压之后，需要修改MAT的配置文件，打开MemoryAnalyzer.ini文件，修改-Xmx的值，使其大于hprof文件的大小，建议是其2倍大小。

**执行分析命令**

```sh
./ParseHeapDump.sh heap.hprof org.eclipse.mat.api:suspects
```

执行以后会生成 xxxx\_Suspects.zip 的压缩包，解压后打开其中的html页面即可看到结果。
