工作中虽然不经常遇到java.lang.OutOfMemoryError: Java heap space,但一旦出现将会严重影响系统运行,如果不能迅速解决,将会带来很恶劣的影响,所以掌握OutOfMemoryError:Java heap space的解决方法也是非常有必要的。
解决问题首先要找出原因,要找原因要有依据,而内存溢出时的堆dump信息是最为重要的依据之一
步骤一:先保证能拿到堆dump信息
我写了一个简单的小例子,jvm运行参数为:-Xmx20M -Xms20M -XX:+HeapDumpOnOutOfMemoryError
public class HeapOutOfMemoryTest {
public static void main(String args[]) throws InterruptedException {
List<HeapOutOfMemoryTest> list = new ArrayList<HeapOutOfMemoryTest>();
Thread.sleep(10000);
for(int i=0;i <3; i++) {
Thread.sleep(10);
new Thread(new MyThread(), "Thread-" + i).start();
}
}
}
class MyThread implements Runnable{
@Override
public void run() {
List<byte[]> list = new ArrayList<byte[]>();
while(true) {
byte[] b = new byte[100 * 1024];
list.add(b);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
程序执行后很快就会抛出内存溢出错误
Exception in thread "Thread-0" Exception in thread "Thread-2" java.lang.OutOfMemoryError: Java heap space
at li.cheng.jvm.chapter3.MyThread.run(HeapOutOfMemoryTest.java:23)
at java.lang.Thread.run(Unknown Source)
java.lang.OutOfMemoryError: Java heap space
at li.cheng.jvm.chapter3.MyThread.run(HeapOutOfMemoryTest.java:23)
at java.lang.Thread.run(Unknown Source)
其实通过异常信息基本已经可以锁定内存溢出原因,但为了解决问题思路具有通用性,我们继续往下看
步骤二:分析堆内存溢出时的dump文件
堆dump文件可以直接下载我的dump文件,用Eclipse Memory Analyzer或者VisualVM打开,我用的是Eclipse Memory Analyzer
可以看到这两个线程占的内存总量达到了近90%
再进一步展开Thread-2的堆信息
可以看到有大量的byte数组,就是我们代码中新建的数组,正是因为这个原因导致内存溢出。当然在实际业务代码中能看到具体的业务类名称,基本上就一目了然了。
步骤三:如果堆dump信息也正常怎么办?
可能经过上面两个步骤的分析,虽然确实由于堆中对象太多导致内存溢出,但都是正常的对象,没有某个类的对象产生特别多的情况,应该怎么办?
这时候就考虑增大堆内存的大小,或者减少对象的产生
方法1:直接增加堆内存大小,调整-Xmx -Xms 的大小
方法2:本地缓存改为集中缓存。大量使用本地缓存(如大量使用HashMap作为K/V缓存),会占用堆上很多内存,尤其在集群情况下,会造成更多浪费,这时可以考虑把本地缓存改为集中缓存(如Redis)。
方法3:考虑优化代码,看是否有优化的空间。