OutOfMemoryError异常实战

来源:互联网 发布:淘宝卖家如何上传视频 编辑:程序博客网 时间:2024/06/05 18:00

实战:OutOfMemoryError异常

1. Java堆溢出

Java堆用于存储对象实例,我们只要不断创建对象,并且保证GC Roots到对象之间有可达路径来避免GC清除这些对象,就会在对象数量到达最大堆的容量限制后产生内存溢出异常。

VM Args: -Xms10m -Xmx10m
-XX:+HeapDumpOnOutOfMemoryError

XX:+HeapDumpOnOutOfMemoryError这个参数可以让虚拟机在出现内存溢出异常时Dump出当前的内存堆转储快照以便事后进行分析。

import java.util.ArrayList;
import java.util.List;
/**
* VM Args: -Xms10m -Xmx10m -XX:+HeapDumpOnOutOfMemoryError
*/
public class HeapOOM {
static class OOMObject{
private String name;
public OOMObject(String name) {
this.name = name;
}
}
public static void main(String[] args) {
List list = new ArrayList();
long i = 1;
while(true) {
list.add(new OOMObject(“IpConfig…” + i++));
}
}
}
抛出的异常:

Dumping heap to java_pid27828.hprof …
Heap dump file created [14123367 bytes in 0.187 secs]
Exception in thread “main” java.lang.OutOfMemoryError: Java heap space
at java.lang.AbstractStringBuilder.<init>(AbstractStringBuilder.java:45)
at java.lang.StringBuilder.<init>(StringBuilder.java:92)
at com.baoxian.HeapOOM.main(HeapOOM.java:22)
注:出现Java堆内存溢出时,异常堆栈信息java.lang.OutOfMemoryError后面会紧跟着JavaHeapSpace。

要解决这个异常,一般手段是首先通过内存映像分析工具比如Eclipse Memory Analyzer对dump出来的堆转储快照进行分析,重点是确认内存中对象是否是必要的,也就是要弄清楚到底是出现了内存泄露Memory Leak还是内存溢出Memory Overflow。

如果是内存泄露,可进一步通过工具查看泄露对象到GC Roots的引用链。于是就能找到泄露对象时通过怎样的路径与GC Roots相关联并导致垃圾收集器无法自动回收它们。掌握了泄露对象的类型信息,以及GC Roots引用链的信息,就可以比较准确的定位出泄露代码的位置了。

如果不存在泄露,那么就该修改-Xms和-Xms堆参数看能否加大点。

2、虚拟机栈和本地方法栈溢出
-Xoss参数设置本地方法栈大小,对于HotSpot没用。栈容量只由-Xss参数设定。

/**
* VM Args: -Xss128k
* @author Administrator
*
*/
public class JavaVMStackSOF {
private int stackLength = 1;
public void stackLeak() {
stackLength++;
stackLeak();
}
public static void main(String[] args) throws Throwable{
JavaVMStackSOF oom = new JavaVMStackSOF();
try {
oom.stackLeak();
} catch (Throwable e) {
System.out.println(“stack length: ” + oom.stackLength);
throw e;
}
}
}
抛出异常:

stack length: 1007
Exception in thread “main” java.lang.StackOverflowError
at com.baoxian.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:11)
at com.baoxian.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:12)
at com.baoxian.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:12)
at com.baoxian.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:12)
3、运行时常量池溢出
运行时常量池分配在方法区内,可以通过-XX:PermSize和-XX:MaxPermSize限制方法区大小,从而间接限制其中常量池的容量。

import java.util.ArrayList;
import java.util.List;
/**
* VM Args: -XX:PermSize=10M -XX:MaxPermSize=10M
*/
public class RuntimeConstantPoolOOM {
public static void main(String[] args) {
// 使用List保持着常量池引用,避免Full GC回收常量池行为
List list = new ArrayList;();
// 10MB的PermSize在integer范围内足够产生OOM了
int i = 0;
while (true) {
list.add(String.valueOf(i++).intern());
}
}
}
异常:

Exception in thread “main” java.lang.OutOfMemoryError: PermGen space
at java.lang.String.intern(Native Method)
at com.baoxian.RuntimeConstantPoolOOM.main(RuntimeConstantPoolOOM.java:18)
运行时常量池溢出,在java.lang.OutOfMemoryError后面紧跟着是PermGen space

4、方法区溢出
方法区用于存放Class的相关信息,如类名、访问修饰符、常量池、字段描述符、方法描述等。对于这个区域的测试,基本的思路是运行时产生大量的类去填满方法区,直到溢出。比如动态代理会生成动态类。

使用CGLib技术直接操作字节码运行,生成大量的动态类。当前很多主流框架如Spring和Hibernate对类进行增强都会使用CGLib这类字节码技术,增强的类越多,就需要越大的方法区来保证动态生成的Class可以加载入内存。

异常:

Exception in thread “main” java.lang.OutOfMemoryError: PermGen space at java.lang.String.intern(Native Method)
同样,跟常量池一样,都是PermGen space字符串出现

方法区溢出也是一种常见的内存溢出异常,一个类如果要被垃圾收集器回收,判定条件是非常苛刻的。在经常动态生成大量Class的应用中,需要特别注意类的回收状况。这类场景除了上面提到的程序使用GCLib字节码技术外,常见的还有:大量JSP或动态产生的JSP文件的应用(JSP第一次运行时需要编译为Java类)、基于OSGi应用等。

5、本机直接内存溢出
DirectMemory容量可以通过-XX:MaxDirectMemorySize指定,如果不指定,则默认与Java堆的最大值-Xmx指定一样。

/**
* VM Args: -Xmx20M -XX:MaxDirectMemorySize=10M
*/
public class DirectMemoryOOM {
private static final int _1MB = 1024 * 1024;
public static void main(String[] args) {
Field unsafeField = Unsafe.class.getDeclaredFields()[0];
unsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe) unsafeField.get(null);
while(true) {
unsafe.allocateMemory(_1MB);
}
}
}
在OutOfMemoryError后面不会有任何东西了,这就是DirectMemory内存溢出了。

0 0
原创粉丝点击