参数 说明
-XX:+HeapDumpOnOutOfMemoryError 内存溢出时自动导出内存快照
-XX:HeapDumpPath=E:/dumps/ 导出内存快照时保存的路径
Copy
/**
* Java堆内存溢出异常
* VM args: -Xms20M -Xmx20M -XX:+HeapDumpOnOutOfMemoryError
* -Xms和-Xmx设为相同值避免堆内存自动扩展,
* -XX:+HeapDumpOnOutOfMemoryError可以让虚拟机在发生OOM时Dump出内存快照
* Run With JDK 1.8
* */
public class HeapOOM {
static class OOMObject{
}
public static void main(String[] args){
List<OOMObject> list = new ArrayList<>();
while(true){
list.add(new OOMObject());
}
}
}
运行结果:
Copy
java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid1344.hprof ...
Heap dump file created [29068691 bytes in 0.108 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3210)
at java.util.Arrays.copyOf(Arrays.java:3181)
at java.util.ArrayList.grow(ArrayList.java:261)
at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:235)
at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:227)
at java.util.ArrayList.add(ArrayList.java:458)
at test.oom.HeapOOM.main(HeapOOM.java:21)
可以从异常信息中看到,OOM异常发生在“main”线程,发生的内存区域是“Java heap space”。
Copy
stack length: 998
Exception in thread "main" java.lang.StackOverflowError
at com.cellei.outofmemory.StackOOM.stackLeak(StackOOM.java:15)
at com.cellei.outofmemory.StackOOM.stackLeak(StackOOM.java:16)
at com.cellei.outofmemory.StackOOM.stackLeak(StackOOM.java:16)
...
at com.cellei.outofmemory.StackOOM.main(StackOOM.java:22)
实验结果表明,不论是减小栈容量大小还是增加栈帧大小,当内存无法分配时虚拟机抛出的都是StackOverflowError异常。
Copy
/**
* 要求运行在 JDK1.6 或以前
* 导致常量池溢出从而产生永久代溢出
* VM Args: -XX:PermSize=10M -XX:MaxPermSize=10M
* Run With JDK 1.6
*/
public class ConstantPoolOverflowTest
{
public static void main(String[] args)
{
List<String> list = new ArrayList<String>();
int i = 0;
while (true)
{
list.add(String.valueOf(i++).intern());
}
}
}
运行结果:
Copy
Exception in thread "main" java.lang.OutOfMemoryError: PermGen space
at java.lang.String.intern(Native Method)
...
可见运行结果提示了PermGen space,表明是那个版本的永久代也就是方法区溢出。
Copy
/**
* 限制元空间大小后
* 使用CGLib运行时产生类,导致元空间也就是方法区溢出
* VM Args:-XX:MetaspaceSize=8M -XX:MaxMetaspaceSize=28M
* Run With JDK 1.8
*/
public class MethodAreaOOM {
static class OOMObject{
}
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 o, Method method, Object[] objects,
MethodProxy methodProxy) throws Throwable {
return methodProxy.invokeSuper(o, objects);
}
});
enhancer.create();
}
}
}
运行结果:
Copy
Exception in thread "main" java.lang.OutOfMemoryError: Metaspace
at net.sf.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:345)
at net.sf.cglib.proxy.Enhancer.generate(Enhancer.java:492)
at net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:114)
at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:291)
at net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:480)
at net.sf.cglib.proxy.Enhancer.create(Enhancer.java:305)
at com.cellei.oom.MethodAreaOOM.main(MethodAreaOOM.java:29)
可见异常信息提示Metaspace,就是说元空间(方法区)内存溢出了。方法区溢出也是一种比较常见的溢出,一个类要被垃圾收集器回收,判定条件是比较苛刻的。在经常动态产生大量 Class 的应用中,要特别注意类的回收情况。
Copy
/**
* 不断的申请内存,导致本机内存溢出
* VM Args: -Xmx20M -XX:MaxDirectMemorySize=10M
* Run With JDK 1.8
* */
public class DirectMemoryOOM {
private static final int _1M = 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(_1M);
}
}
}
运行结果:
Copy
Exception in thread "main" java.lang.OutOfMemoryError
at sun.misc.Unsafe.allocateMemory(Native Method)
at com.cellei.oom.DirectMemoryOOM.main(DirectMemoryOOM.java:20)
由DirectMemory导致的内存溢出,有一个特点就是Heap Dump文件中不会看到明显异常,如果Dump文件非常小,又直接间接使用了NIO,则有可能是这方面的原因。