经过简单的小例子程序,演示java虚拟机各部份内存溢出状况:java
(1).java堆溢出:web
Java堆用于存储实例对象,只要不断建立对象,而且保证GC Roots到对象之间有引用的可达,避免垃圾收集器回收实例对象,就会在对象数量达到堆最大容量时产生OutOfMemoryError异常。多线程
想要方便快速地产生堆溢出,要使用以下java虚拟机参数:-Xms10m(最小堆内存为10MB),-Xmx10m(最大堆内存为10MB,最小堆内存和最大堆内存相同是为了不堆动态扩展),-XX:+HeapDumpOnOutOfMemoryError可让java虚拟机在出现内存溢出时产生当前堆内存快照以便进行异常分析。app
例子代码以下:ide
运行一段时间就会发现产生OutOfMemoryError异常,而且产生了堆内存异常dump文件。oop
(2).java虚拟机栈和本地方法栈溢出:url
因为Sun的HotSpot虚拟机不区分java虚拟机栈和本地方法栈,所以对于HotSpot虚拟机来讲-Xoss参数(设置本地方法栈大小)虽然存在,可是其实是无效的,栈容量只能由-Xss参数设定。spa
因为Java虚拟机栈会出现StackOverflowError和OutOfMemoryError两种异常,因此分别使用两个例子演示这两种状况:操作系统
a.java虚拟机栈深度溢出:.net
单线程的环境下,不管是因为栈帧太大,仍是虚拟机栈容量过小,当内存没法再分配的时候,虚拟机总抛出StackOverflowError异常。使用-Xss128k将java虚拟机栈大小设置为128kb,例子代码以下:
运行一段时间后,产生StackOverflowError异常。Java虚拟机栈溢出通常会产生在方法递归调用过多而java虚拟机栈内存不够的状况下。
b.java虚拟机栈内存溢出:
多线程环境下,可以建立的线程最大内存=物理内存-最大堆内存-最大方法区内存,在java虚拟机栈内存必定的状况下,单个线程占用的内存越大,所能建立的线程数目越小,因此在多线程条件下很容易产生java虚拟机栈内存溢出的异常。
使用-Xss2m参数设置java虚拟机栈内存大小为2MB,例子代码以下:
运行一段时间以后,java虚拟机栈就会由于内存过小没法建立线程而产生OutOfMemoryError。
(3).运行时常量池溢出:
运行时常量池属于方法区的一部分,能够使用-XX:PermSize=10m和-XX:MaxPermSize=10m将永久代最大内存和最小内存设置为10MB大小,而且因为永久代最大内存和最小内存大小相同,所以没法扩展。
String的intern()方法用于检查常量池中若是有等于此String对象的字符串存在,则直接返回常量池中的字符串对象,不然,将此String对象所包含的字符串添加到运行时常量池中,并返回此String对象的引用。所以String的intern()方法特别适合演示运行时常量池溢出,例子代码以下:
运行一段时间,永久代内存不够,运行时常量池因没法再添加常量而产生OutOfMemoryError。
(4).方法区溢出:
运行时常量池是方法区的一部分,他们都属于HotSpot虚拟机中的永久代内存区域。方法区用于存放Class的相关信息,Java的反射和动态代理能够动态产生Class,另外第三方的CGLIB能够直接操做字节码,也能够动态产生Class,实验经过CGLIB来演示,一样使用-XX:PermSize=10m和-XX:MaxPermSize=10m将永久代最大内存和最小内存设置为10MB大小,而且因为永久代最大内存和最小内存大小相同,所以没法扩展。例子代码以下:
运行一段时间以后,永久代内存不够,方法区没法再存放CGLIB建立处理的Class信息,产生方法区OutOfMemoryError。
(5).本机直接内存溢出:
Java虚拟机能够经过参数-XX:MaxDirectMemorySize设定本机直接内存可用大小,若是不指定,则默认与java堆内存大小相同。JDK中能够经过反射获取Unsafe类(Unsafe的getUnsafe()方法只有启动类加载器Bootstrap才能返回实例)直接操做本机直接内存。经过使用-XX:MaxDirectMemorySize=10M,限制最大可以使用的本机直接内存大小为10MB,例子代码以下:
当运行一段时间以后,10MB的本机直接内存被分配光,没法在进行直接内存分配时,产生OutOfMemoryError。