【Java虚拟机】内存溢出实战

在Java虚拟机规范的描述中,除了程序计数器外,虚拟机内存的其他几个运行时区域都有发生OutOfMemoryError(下文称OOM)异常的可能,本节将通过若干实例来验证异常发生的场景。
测试异常需要设置虚拟机的启动参数,以MyEclipse工具为例,修改如下:Run——>Run Configurations;(修改的参数在代码顶部注释中,这些参数将对实验的结果有直接影响)


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

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import java.util.ArrayList; import java.util.List; /** * -Xms20M -Xmx20M -XX:+HeapDumpOnOutOfMemoryError * 将堆最小值参数-Xms与最大值-Xmx参数设置为一样即可避免堆自动扩展, * @author Vanguard */public class TestGC { static class OOMObject{ } public static void main(String[] args) { List list = new ArrayList<>(); while ( true ){ list.add( new OOMObject()); } } }
运行结果为:

OutOfMemoryError后紧跟着Java heap space提示堆溢出。
栈溢出
由于在HotSpot虚拟机中并不区分虚拟机栈和本地方法栈,因此,对于HotSpot来说,虽然-Xoss参数(设置本地方法栈大小)存在,但实际上是无效的,栈容量只由-Xss参数设定。
关于虚拟机栈和本地方法栈,在Java虚拟机规范中描述了两种异常:

  • 如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError异常。

  • 如果虚拟机在扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 /** * -Xss128k * @author Vanguard */ public class JavaVMStackSOF { private int stackLength= 1 ; public void stackLeak(){ stackLength++; stackLeak(); } public static void main(String[] args) { JavaVMStackSOF oom = new JavaVMStackSOF(); try { oom.stackLeak(); } catch (Throwable e){ System.out.println( "stack length:" +oom.stackLength); throw e; } } }
运行结果:

方法区溢出
运行时常量池是方法区的一部分,因此这两个区域的溢出测试就放在一起进行。
String.intern()是一个Native方法,它的作用是:如果字符串常量池中已经包含一个等于此String对象的字符串,则返回代表池中这个字符串的String对象;否则,将此String对象包含的字符串添加到常量池中,并且返回此String对象的引用。
方法区用于存放Class的相关信息,如类名、访问修饰符、常量池、字段描述、方法描述等。对于这些区域的测试,基本的思路是运行时产生大量的类去填满方法区,直到溢出。

在实际应用中,当前的很多主流框架,如Spring、Hibernate,在对类进行增强时都会用到CGLib这类字节码技术,增强的类越多,就需要越大的方法区来保证动态生成的Class可以加载入内存,若内存不足则引发异常。
【【Java虚拟机】内存溢出实战】转自:http://www.yexiaoquan.com/article/17061301.html

    推荐阅读