通过配置Jvm参数结合代码重现OutOfMemoryError异常
1. Java堆溢出
Java虚拟机参数:-Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError
static class OOMObject {
}
public static void main(String[] args) {
List<OOMObject> list = new ArrayList<OOMObject>();
while (true) {
list.add(new OOMObject());
}
}
}
运行结果:
java.lang.OutOfMemoryError: Java heap space
2. 虚拟机栈和本地方法栈溢出
Java虚拟机参数:-Xss128k
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:2402
Exception in thread "main" java.lang.*Error
然而在单线程下,使用-Xss参数减少栈内存容量和定义大量本地变量,都抛出*Error
在多线程下,通过不断创建线程,使内存分配耗尽,抛出OutOfMemoryError,代码如下(谨慎使用):
Java虚拟机参数:-Xss2M (这时候不妨设大些)
public class JavaVMStackOOM {
private void dontStop() {
while (true) {
}
}
public void stackLeakByThread() {
while (true) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
dontStop();
}
});
thread.start();
}
}
public static void main(String[] args) throws Throwable {
JavaVMStackOOM oom = new JavaVMStackOOM();
oom.stackLeakByThread();
}
}
3. 方法区和运行时常量池溢出
String.intern()是一个Native方法,作用是:如果字符串常量池中已经包含一个等于此String对象的字符串,则返回代表池中这个字符串的String对象;否则,将此对象包含的字符串添加到常量池,并返回此String对象的引用。
Java虚拟机参数:-XX:PermSize=10M -XX:MaxPermSize=10M
public class RuntimeConstantPoolOOM {
public static void main(String[] args) {
// 使用List保持着常量池引用,避免Full GC回收常量池行为
List<String> list = new ArrayList<String>();
// 10MB的PermSize在integer范围内足够产生OOM了
int i = 0;
while (true) {
list.add(String.valueOf(i++).intern());
}
}
}
运行结果:
Exception in thread "main" java.lang.OutOfMemoryError:PermGen space
方法区用于存放Class的相关信息,比如类名、访问修饰符、常量池、字段描述、方法描述等。
对于这些区域的测试,基本思路是运行时产生大量的类去填满方法区,直到溢出。下面的代码利用GCLib直接操作字节码在运行时生成了大量的动态类。
Java虚拟机参数: -XX:PermSize=10M -XX:MaxPermSize=10M
public class JavaMethodAreaOOM {
public static void main(String[] args) {
while (true) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(OOMObject.class);
enhancer.setUseCache(false);
enhancer.setCallback(new MethodInterceptor() {
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
return proxy.invokeSuper(obj, args);
}
});
enhancer.create();
}
}
static class OOMObject {
}
}
运行结果:
Exception in thread "main" java.lang.OutOfMemoryError:PermGen space
4. 直接内存溢出
DirectMemory容量通过-XX:MaxDirectMemorySize指定,如果不指定,则默认与Java堆最大值(-Xmx指定)一样。下面的代码中,真正申请分配内存的方法是unsafe.allocateMemory()
Java虚拟机参数:-Xmx20M -XX:MaxDirectMemorySize=10M
public class DirectMemoryOOM {
private static final int _1MB = 1024 * 1024;
public static void main(String[] args) throws Exception {
Field unsafeField = Unsafe.class.getDeclaredFields()[0];
unsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe) unsafeField.get(null);
while (true) {
unsafe.allocateMemory(_1MB);
}
}
}
运行结果:
Exception in thread "main" java.lang.OutOfMemoryError