Java虚拟机内存区域溢出测试
测试内存溢出前,先配置Debug configurations,在Arguments中的VM arguments文本框中输入对应的虚拟机启动参数。然后在进行Run执行。这样可以让虚拟机在出现内存溢出异常时Dump当前的内存堆转储快照以便事后进行分析。
Java堆溢出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
import java.util.ArrayList; import java.util.List; /** * VM Args: -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError * @author Rain * */ public class heapOOM { static class OOMObject{ } public static void main(String[] args){ List<OOMObject> list = new ArrayList<OOMObject>(); while(true){ list.add(new OOMObject()); } } } |
运行结果
1 2 3 4 |
java.lang.OutOfMemoryError: Java heap space Dumping heap to java_pid4572.hprof ... Heap dump file created [28022194 bytes in 0.087 secs] |
Java堆内存的OOM异常是实际应用中常见的内存溢出异常情况。因为Java堆用于存储对象实例,只要不断的创建对象,超过最大堆的容量限制必然溢出,当出现Java堆内存溢出时,异常堆栈信息提示:java.lang.OutOfMemoryError: Java heap space
虚拟机栈和本地方法栈溢出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
/** * VM Args:-Xss128k * @author Rain * */ 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; } } } |
输出结果:
1 2 3 |
stack length:19479 Exception in thread "main" java.lang.StackOverflowError |
因为内存分配原则,虚拟机栈和本机方法栈是最后分配内存的,每个线程分配到的栈容量越大,可以建立的线程数量自然越少,建立线程时就越容易把剩下的内存耗尽。如果使用虚拟机默认参数,栈深度在大多数情况下达到1000~2000完全没有问题,正常的方法调用和递归是够用了,但是在建立过多线程导致的内存溢出,在不能减少线程数或者更换64位虚拟机的情况下,就只能减少最大堆和减少栈容量来换取更多的线程。
方法区和运行时常量池溢出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
import java.util.ArrayList; import java.util.List; /** * VM Args: -XX:PermSize=10M -XX:MaxPermSize=10M * @author Rain * */ 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()); } } } |