OOM(OutOfMemoryError)是Java运行时产生的内存溢出错误。它会在JVM在试图分配内存的时候产生,提示JVM内存空间无法满足需求。
OOM产生的主要原因有:
- 堆内存溢出:当对象实例数量过多导致堆内存不足时产生。常见场景是大量对象被保存在集合中没有及时释放。
- 栈内存溢出:当线程数量过多或递归调用层数过深导致栈内存耗尽时产生。
- 方法区溢出:当加载过多类或常量池内容过多导致方法区内存不足时产生。常见场景是大量动态代理类的产生。
- 本地内存溢出:当本地内存任意使用(如IO操作)导致本地内存耗尽时产生。
来看一个简单例子:我们可以编写几个Java程序模拟这几种OOM的产生,以便加深理解。
堆内存溢出:
public class HeapOOM {
public static void main(String[] args) {
List<Object> list = new ArrayList<>();
while (true) {
list.add(new Object());
}
}
}
栈内存溢出:
public class StackOOM {
public void recursiveCall() {
recursiveCall();
}
public static void main(String[] args) {
StackOOM oom = new StackOOM();
oom.recursiveCall();
}
}
方法区溢出:
public class MethodAreaOOM {
public static void main(String[] args) {
while (true) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(MethodAreaOOM.class);
enhancer.setUseCache(false);
enhancer.create();
}
}
}
本地内存溢出:
public class NativeOOM {
public static void main(String[] args) throws Exception {
ByteBuffer buffer = ByteBuffer.allocateDirect(1024*1024*1024);
while (true) {
buffer.put(new byte[1024*1024]);
}
}
}
要避免OOM的发生,我们需要:
- 及时释放不再使用的对象引用。
- 避免过度嵌套递归调用,保持栈桢数在适当范围。
- 避免动态创建过多类,特别是动态代理类。
- 避免直接内存分配过量,可以使用堆内存作为替代。
- 合理配置JVM内存参数,特别是堆内存和栈内存的大小。