《深入理解java虚拟机》学习笔记2——Java内存溢出实例

转载 2013年12月04日 15:31:46

通过简单的小例子程序,演示java虚拟机各部分内存溢出情况:

(1).java堆溢出:

Java堆用于存储实例对象,只要不断创建对象,并且保证GC Roots到对象之间有引用的可达,避免垃圾收集器回收实例对象,就会在对象数量达到堆最大容量时产生OutOfMemoryError异常。

想要方便快速地产生堆溢出,要使用如下java虚拟机参数:-Xms10m(最小堆内存为10MB),-Xmx10m(最大堆内存为10MB,最小堆内存和最大堆内存相同是为了避免堆动态扩展),-XX:+HeapDumpOnOutOfMemoryError可以让java虚拟机在出现内存溢出时产生当前堆内存快照以便进行异常分析。

例子代码如下:

[java] view plaincopy
  1. public class HeapOOM{  
  2.     static class OOMObject{  
  3. }  
  4. public static void main(String[] args){  
  5.     List<OOMObject> list = new ArrayList<OOMObject>();  
  6.     while(true){  
  7.     list.add(new OOMObject());  
  8. }  
  9. }  
  10. }  

运行一段时间就会发现产生OutOfMemoryError异常,并且产生了堆内存异常dump文件。

(2).java虚拟机栈和本地方法栈溢出:

由于Sun的HotSpot虚拟机不区分java虚拟机栈和本地方法栈,因此对于HotSpot虚拟机来说-Xoss参数(设置本地方法栈大小)虽然存在,但是实际上是无效的,栈容量只能由-Xss参数设定。

由于Java虚拟机栈会出现StackOverflowError和OutOfMemoryError两种异常,所以分别使用两个例子演示这两种情况:

a.java虚拟机栈深度溢出:

单线程的环境下,无论是由于栈帧太大,还是虚拟机栈容量太小,当内存无法再分配的时候,虚拟机总抛出StackOverflowError异常。使用-Xss128k将java虚拟机栈大小设置为128kb,例子代码如下:

[java] view plaincopy
  1. public class JavaVMStackOF{  
  2.     private int stackLength = 1;  
  3.     public void stackLeak(){  
  4.         statckLength++;  
  5.         stackLeak();  
  6. }  
  7. public static void main(String[] args){  
  8.     JavaVMStackOF oom = new JavaVMStackOF();  
  9. oom.stackLeak();  
  10. }  
  11. }  

运行一段时间后,产生StackOverflowError异常。Java虚拟机栈溢出一般会产生在方法递归调用过多而java虚拟机栈内存不够的情况下。

b.java虚拟机栈内存溢出:

多线程环境下,能够创建的线程最大内存=物理内存-最大堆内存-最大方法区内存,在java虚拟机栈内存一定的情况下,单个线程占用的内存越大,所能创建的线程数目越小,所以在多线程条件下很容易产生java虚拟机栈内存溢出的异常。

使用-Xss2m参数设置java虚拟机栈内存大小为2MB,例子代码如下:

[java] view plaincopy
  1. public class JavaVMStackOOM{  
  2.     private void dontStop(){  
  3.     while(true){  
  4. }  
  5. }  
  6. public void stackLeakByThread{  
  7.     while(true){  
  8.         Thread t = new Thread(new Runnable(){  
  9.     public void run(){  
  10.     dontStop();  
  11. }  
  12. });  
  13. t.start();  
  14. }  
  15. }   
  16. public static void main(String[] args){  
  17.     JavaVMStackOOM oom = new JavaVMStackOOM();  
  18.     oom. stackLeakByThread();.  
  19. }  
  20. }  

运行一段时间之后,java虚拟机栈就会因为内存太小无法创建线程而产生OutOfMemoryError。

(3).运行时常量池溢出:

运行时常量池属于方法区的一部分,可以使用-XX:PermSize=10m和-XX:MaxPermSize=10m将永久代最大内存和最小内存设置为10MB大小,并且由于永久代最大内存和最小内存大小相同,因此无法扩展。

String的intern()方法用于检查常量池中如果有等于此String对象的字符串存在,则直接返回常量池中的字符串对象,否则,将此String对象所包含的字符串添加到运行时常量池中,并返回此String对象的引用。因此String的intern()方法特别适合演示运行时常量池溢出,例子代码如下:

[java] view plaincopy
  1. public class RuntimeConstantPoolOOM{  
  2.     public static void main(String[] args){  
  3. List<String> list = new ArrayList<String>();  
  4.         int i = 0;  
  5.         while(true){  
  6.         list.add(String.valueOf(i++).intern());  
  7. }  
  8. }  
  9. }  

运行一段时间,永久代内存不够,运行时常量池因无法再添加常量而产生OutOfMemoryError。

(4).方法区溢出:

运行时常量池是方法区的一部分,他们都属于HotSpot虚拟机中的永久代内存区域。方法区用于存放Class的相关信息,Java的反射和动态代理可以动态产生Class,另外第三方的CGLIB可以直接操作字节码,也可以动态产生Class,实验通过CGLIB来演示,同样使用-XX:PermSize=10m和-XX:MaxPermSize=10m将永久代最大内存和最小内存设置为10MB大小,并且由于永久代最大内存和最小内存大小相同,因此无法扩展。例子代码如下:

[java] view plaincopy
  1. public class JavaMethodAreaOOM{  
  2.     public static void main(String[] args){  
  3.     while(true){  
  4.     Enhancer enhancer = new Enhancer();  
  5.     enhancer.setSuperClass(OOMObject.class);  
  6.     enhancer.setUseCache(false);  
  7.     enhancer.setCallback(new MethodInterceptor(){  
  8.     public Object intercept(Object obj, Method method, Object[] args,   
  9.                       MethodProxy proxy)throws Throwable{  
  10.     return proxy.invokeSuper(obj, args);  
  11. }  
  12. });  
  13. enhancer.create();  
  14. }  
  15. }  
  16. class OOMObject{  
  17. }   
  18. }  

运行一段时间之后,永久代内存不够,方法区无法再存放CGLIB创建处理的Class信息,产生方法区OutOfMemoryError。

(5).本机直接内存溢出:

Java虚拟机可以通过参数-XX:MaxDirectMemorySize设定本机直接内存可用大小,如果不指定,则默认与java堆内存大小相同。JDK中可以通过反射获取Unsafe类(Unsafe的getUnsafe()方法只有启动类加载器Bootstrap才能返回实例)直接操作本机直接内存。通过使用-XX:MaxDirectMemorySize=10M,限制最大可使用的本机直接内存大小为10MB,例子代码如下:

[java] view plaincopy
  1. public class DirectMemoryOOM{  
  2.     private static final int _1MB = 10241024 * 1024;  
  3.     publc static void main(String[] args) throws Exception{  
  4.         Field unsafeField = Unsafe.class.getDeclaredFields()[0];  
  5.         unsafeField.setAccessible(true);  
  6.         Unsafe unsafe = (Unsafe) unsafeField.get(null);  
  7.         while(true){  
  8.             //unsafe直接想操作系统申请内存  
  9.     unsafe.allocateMemory(_1MB);  
  10. }  
  11. }  
  12. }  

当运行一段时间之后,10MB的本机直接内存被分配光,无法在进行直接内存分配时,产生OutOfMemoryError。

相关文章推荐

《深入理解java虚拟机》学习笔记2——Java内存溢出实例

通过简单的小例子程序,演示java虚拟机各部分内存溢出情况: (1).java堆溢出: Java堆用于存储实例对象,只要不断创建对象,并且保证GC Roots到对象之间有引用的可达,避免垃圾收集器...

《深入理解java虚拟机》学习笔记2——Java内存溢出实例

通过简单的小例子程序,演示java虚拟机各部分内存溢出情况: (1).java堆溢出: Java堆用于存储实例对象,只要不断创建对象,并且保证GC Roots到对象之间有引用的可达,避免垃圾收集器回收...

《深入理解Java虚拟机》学习笔记——内存区域

Java虚拟机在执行程序过程中会把管理的内存划分为不同的shuj
  • xu_fu
  • xu_fu
  • 2014年08月27日 07:22
  • 959

深入理解Java虚拟机学习笔记——二、垃圾收集器与内存分配策略

1、对象是否存活判定算法 引用计数算法 给对象添加一个引用计数器,当有其他对象引用它时,计数器加1;当引用失效时,计数器减1。任何时刻计数器为0的对象就是不可能在被使用的。引用计数算法实现简单,判...

《深入理解Java虚拟机——JVM高级特性与最佳实践》学习笔记——自动内存管理机制

《深入理解Java虚拟机——JVM高级特性与最佳实践》学习笔记——自动内存管理机制1.概述对于从事C、C++程序开发的开发人员来说,在内存管理领域,既拥有每一个对象的”所有权”,又担负着每一个对象生命...

深入理解java虚拟机学习笔记2-java内存区域

对于java程序员来说,在java 的自动内存管理机制的帮助下,不再需要为每个new操作去写配对的delete/free代码,而且不容易出现内存泄漏和内存溢出的问题,看起来由虚拟机管理内存一切都很美好...

《深入理解java虚拟机》学习笔记1——Java内存结构

java虚拟机规范规定的java虚拟机内存其实就是java虚拟机运行时数据区,其架构如下: 其中方法区和堆是由所有线程共享的数据区。 Java虚拟机栈,本地方法栈和程序计数器是线程隔离的数...

《深入理解java虚拟机》学习笔记1——Java内存结构

Java虚拟机规范规定的java虚拟机内存其实就是java虚拟机运行时数据区,其架构如下: 其中方法区和堆是由所有线程共享的数据区。 Java虚拟机栈,本地方法栈和程序计数器是线程隔离的数据区...

《深入理解java虚拟机》学习笔记1——Java内存结构

java虚拟机规范规定的java虚拟机内存其实就是java虚拟机运行时数据区,其架构如下: 其中方法区和堆是由所有线程共享的数据区。 Java虚拟机栈,本地方法栈和程序计数器是线程...

深入理解Java虚拟机学习笔记-1.JVM内存模型

JVM内存模型1.内存模型结构图 名称 特征 作用 配置参数 异常 程序计数器 占用内存小,线程私有, 生命周期与线程相同 大致为字节码行号指示器 无 无 虚...
  • cnwuhao
  • cnwuhao
  • 2017年07月27日 20:00
  • 207
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:《深入理解java虚拟机》学习笔记2——Java内存溢出实例
举报原因:
原因补充:

(最多只允许输入30个字)