OOM内存溢出分析

概念与背景

内存溢出(Out Of Memory,简称OOM)是指应用系统中存在无法回收的内存或使用的内存过多,最终使得程序运行要用到的内存大于能提供的最大内存。

通常面试过程中,经常会有面试官问如果生产出现oom问题,你要怎么解决,处理步骤见下面


原因分析

1. 内存泄漏

由于长期保持某些资源的引用,垃圾回收器无法回收它,从而使该资源不能够及时释放,也称为内存泄露。因而尽量不要将所有引用都使用为强引用,可以在合适的地方使用弱引用和软引用。后期讲分析threadlocal的不正确使用导致的内存泄漏问题。

2. 超大对象

保存多个耗用内存过大或当加载单个超大的对象时,该对象的大小超过了当前剩余的可用内存空间。比如查询数据库中的数据,一次查询过多,直接导致内存溢出了。因此查询数据库如果数据过多尽量使用分页查询

3. 死循环无法清除的对象

存在死循环重复产生对象,且集合对象使用完之后依然被引用着,导致无法清除导致占用空间过大等。本文演示此条


演示OOM异常

模拟死循环无法清除的对象导致的oom现象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class OomTest {

public static void main(String[] args) {
// 记录多少次发生oom错误
AtomicInteger atomicInteger = new AtomicInteger(0);
List<Content> contentList = new ArrayList<>();
// 持续放content对象,知道把内存撑爆
while (true){
Content content = new Content();
contentList.add(content);
System.out.println(atomicInteger.incrementAndGet());
}
}
}

class Content{

/**
* 模拟一个1m的对象
*/
byte[] buffer = new byte[1024 * 1024];

}

项目启动时,我们可以添加启动参数,去自动dump出发生异常的内存文件,实际公司中一般发生oom异常,也会由运维同事把当时的dump文件发给你。为加快oom现象的产生,我们调整一下jvm的堆大小为16M。

添加启动参数

-Xms16m -Xmx16m -XX:+HeapDumpOnOutOfMemoryError

点击运行的到报错日志

1
2
3
4
5
6
7
8
9
13
java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid1221.hprof ...
Heap dump file created [14739116 bytes in 0.018 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at com.chenghao.work.oom.Content.<init>(OomTest.java:32)
at com.chenghao.work.oom.OomTest.main(OomTest.java:20)

Process finished with exit code 1

可以发现生成了java_pid1221.hprof文件,生产找运维要即可。


利用JProfile分析

利用jprofile工具打开java_pid1221.hprof,在当前对象集的最大对象可以发现这个ArrayList对象占了百分之96

在图表中可以看到有问题的代码行数和对象调用情况。然后回到代码解决问题