29. troubleshoot之:分析OutOfMemoryError异常
简介
java.lang.OutOfMemoryError应该java应用程序中非常常见的一个的错误了。
那么OutOfMemoryError产生的原因是什么呢?我们怎么去查找相应的错误呢?一起来看看吧。
OutOfMemoryError
先看一下OutOfMemoryError的定义,OutOfMemoryError继承自 VirtualMachineError,它是Error的一种,表示的是应用程序无法处理的异常,一般情况下会导致虚拟机退出。
public class OutOfMemoryError extends VirtualMachineError {
@java.io.Serial
private static final long serialVersionUID = 8228564086184010517L;
/**
* Constructs an {@code OutOfMemoryError} with no detail message.
*/
public OutOfMemoryError() {
super();
}
/**
* Constructs an {@code OutOfMemoryError} with the specified
* detail message.
*
* @param s the detail message.
*/
public OutOfMemoryError(String s) {
super(s);
}
}
一般情形下,如果heap没有更多的空间来分配对象,就会抛出OutOfMemoryError。
还有一种情况是没有足够的native memory来加载java class。
在极少数情况下,如果花费大量时间进行垃圾回收并且只释放了很少的内存,也有可能引发java.lang.OutOfMemoryError。
如果发生OutOfMemoryError,同时会输出相应的stack trace信息。
下面我们分析一下各个不同的OutOfMemoryError。
java.lang.OutOfMemoryError: Java heap space
Java heap space表示的是新对象不能在java heap中分配。
如果遇到这种问题,第一个要想到的解决方法就是去看配置的heap大小是不是太小了。
当然,如果是一个一直都在运行的程序,突然间发生这种问题就要警惕了。因为有可能会存在潜在的内存泄露。需要进一步分析。
还有一种情况,如果java对象实现了finalize方 法,那么该对象在垃圾回收的时候并不会立刻被回收。而是放到一个finalization队列中。
这个队列会由终结器守护线程来执行。如果终结器守护线程的执行速度比对象放入终结器队列中的速度要慢的话,就会导致java对象不能被及时回收。
如果应用程序创建了高优先级的线程,那么高优先级的线程将有可能会导致对象被放入finalization队列的速度比终结器守护线程的处理速度慢。
java.lang.OutOfMemoryError: GC Overhead limit exceeded
GC overhead limit exceeded表示的是GC一直都在运行,从而导致java程序本身执行非常慢。
如果一个java程序98%的时间都在做GC操作,但是只恢复了2%的heap空间,并且持续5次。那么java.lang.OutOfMemoryError将会被抛出。
可以使用下面的参数来关闭这个功能。
-XX:-UseGCOverheadLimit