JVM-OutOfMemory异常重现

来源:互联网 发布:视频后期 软件 编辑:程序博客网 时间:2024/05/16 15:09

原文地址http://blog.csdn.net/zhshulin/article/details/50571469

JVM中常见的OOM,那么如何通过自己编写代码产生这些OOM异常呢?通过写代码重现异常,是为了避免在工作中写出有OOM BUG的代码。之前虽然看过相关文章,但是没自己写过这些代码,这次在编写的实际过程中,由于和书本使用的JDK版本不一致,也会有点问题。其中印象最深刻的就是从JDK1.7开始常量池就已经不放在方法区了,而是改到了Java堆中,所以《深入理解JAVA虚拟机》中的有些知识也需要更新了。下面的代码基于JDK1.7来的。并且在运行程序的时候需要设置JVM参数,如果不设置,轻则需要等待很长时间才会出现异常,重则系统假死甚至导致系统内存溢出

    在测试直接内存的时候,引用了rt.jar中的sun.misc.Unsafe类,如果使用了Eclipse作为IDE,需要修改windows-->preferences-->java-->compiler-->Errors/Warinings,选择Deprecated and restricted API,将Forbidden reference(access rules)修改成ignore。

[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. package org.zsl.learn.oom;  
  2.   
  3. import java.lang.reflect.Field;  
  4. import java.lang.reflect.Method;  
  5. import java.util.ArrayList;  
  6. import java.util.List;  
  7.   
  8.   
  9. import net.sf.cglib.proxy.Enhancer;  
  10. import net.sf.cglib.proxy.MethodInterceptor;  
  11. import net.sf.cglib.proxy.MethodProxy;  
  12. import sun.misc.Unsafe;  
  13.   
  14. /** 
  15.  * 测试在代码中如何产生堆内存溢出、栈溢出(超出长度)、栈内存溢出(栈不能扩展的情况下OOM)、方法区内存溢出、常量池内存溢出 
  16.  * JDK1.7 
  17.  * @author Administrator 
  18.  * 
  19.  */  
  20. public class TestOOM {  
  21.     private static int count = 1;  
  22.     private static final int _1MB = 1024*1024;  
  23.       
  24.     List<String> list = new ArrayList<String>();  
  25.       
  26.     //一个普通的对象  
  27.     static class OOMObjectClass{  
  28.         public OOMObjectClass(){}  
  29.     }  
  30.       
  31.     /** 
  32.      * 通过list对象保持对对象列表的引用,不然GC收集对象,然后不断地向列表中添加新的对象,就会发生OOM 
  33.      *  
  34.      * @VM args:-verbose:gc -Xms10M -Xmx10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+HeapDumpOnOutOfMemoryError 
  35.      */  
  36.     public void testHeapOOM(){  
  37.         List<OOMObjectClass> list = new ArrayList<>();  
  38.         while(true){  
  39.             list.add(new OOMObjectClass());  
  40.         }  
  41.     }  
  42.       
  43.     /** 
  44.      * 通过递归调用方法,从而让方法栈产生栈 StackOverflowError 
  45.      *  
  46.      * @VM args:-verbose:gc -Xss128k 
  47.      */  
  48.     public void stackLeak(){  
  49.         count++;  
  50.         stackLeak();  
  51.     }  
  52.       
  53.       
  54.     /** 
  55.      * 除了上述的递归调用可以产生溢出外,还有就是过多的线程,当栈内存无法动弹扩展是,会出现OOM 
  56.      *  
  57.      * 由于在Window的JVM中,Jave的线程是映射到了操作系统的内核线程上,故而这段代码的运行时非常危险的 
  58.      * 笔者运行的时候限制了JVM内存大小,但是栈内存可以动态扩展,所以电脑内存直接到了90%以上,我果断停止了程序的运行 
  59.      * 由于栈内存只由-Xss参数控制,并没有办法让其不自动扩展,所以这段代码非常危险 
  60.      * 参数:-verbose:gc -Xms10M -Xmx10M -Xss2M 
  61.      */  
  62.     public void stackLeakByThread(){  
  63.         while(true){  
  64.             Thread t = new Thread(new Runnable() {  
  65.                   
  66.                 @Override  
  67.                 public void run() {  
  68.                     while (true){  
  69.                           
  70.                     }  
  71.                 }  
  72.             });  
  73.             t.start();  
  74.             count++;  
  75.         }  
  76.     }  
  77.       
  78.     /** 
  79.      * 常量池是存在于方法区内的,故而只要限制了方法区的大小,当不断新增常量的时候就会发生常量池的溢出 
  80.      *  
  81.      * 笔者使用的是JDK1.7 64位,此时的常量池已经不存在与方法区中,而是迁移到了堆中,故而测试的时候需要限制JVM的堆大小,且不能自动扩展 
  82.      * @VM args: -Xms10M -Xmx10M 
  83.      */  
  84.     public void constantPoolOOM(){  
  85.         int i=0;  
  86.         while(true){  
  87.             list.add(String.valueOf(i++).intern()); //String类型的intern方法是将字符串的值放到常量池中  
  88.         }  
  89.     }  
  90.       
  91.     /** 
  92.      * 方法区是存放一些类的信息等,所以我们可以使用类加载无限循环加载class,这样就会出现方法区的OOM异常 
  93.      * 主要,使用内部类的时候,需要要使用静态内部类,如果使用的是非静态内部类,将不会发生方法区OOM 
  94.      * 使用了CGLib直接操作字节码运行时,生成了大量的动态类 
  95.      * 需要者两个jar包:cglib-2.2.2.jar   asm-3.1.jar 
  96.      * @VM args:-XX:PermSize=10M -XX:MaxPermSize=10M 
  97.      */  
  98.     public void methodAreaOOM(){  
  99.         while(true){  
  100.             Enhancer eh = new Enhancer();  
  101.             eh.setSuperclass(OOMObjectClass.class);  
  102.             eh.setUseCache(false);  
  103.             eh.setCallback(new MethodInterceptor() {  
  104.                 @Override  
  105.                 public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable {  
  106.                     return arg3.invokeSuper(arg0, arg2);  
  107.                 }  
  108.             });  
  109.             eh.create();  
  110.         }  
  111.     }  
  112.       
  113.     /** 
  114.      * 要讨论这部分的内存溢出,首先必须要说一下什么是直接内存: 
  115.      *  直接内存并不是JVM运行时数据区的一部分,也不是JVM规范中定义的内存区域,但是这部分内存也被频繁的使用,也会产生OOM。 
  116.      *  JDK1.4中新加入了NIO类,引入了一种Channel与Buffer的I/O方式,它可以使用Native函数库直接分配堆外内存,然后通过一个存储在JAVA堆里面的DirectByteBuffer对象作为 
  117.      *  这些堆外内存的引用进而操作,这样在某些场景中可以显著的提高性能,避免了在native堆和java堆中来回复制数据。这这部分堆外内存就是直接内存了。 
  118.      *  
  119.      * 直接内存虽然不会受到JAVA堆大小的限制,但是还是会受到本机内存大小的限制,故而服务器管理员在设置JVM内存管理参数的时候,如果忘记了直接内存,那么当程序进行动态扩展的时候,就有可能发生OOM 
  120.      * 直接内存的容量可以通过-XX:MaxDirectMemorySize指定,如果不指定,那么默认与JAVA堆得最大值一样。 
  121.      *  
  122.      * @VM args:-Xmx20M -XX:MaxDirectMemorySize=10M 
  123.      * @throws SecurityException  
  124.      * @throws NoSuchFieldException  
  125.      * @throws IllegalAccessException  
  126.      * @throws IllegalArgumentException  
  127.      */  
  128.     public void directMemoryOOM() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException{  
  129.         Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");  
  130.         unsafeField.setAccessible(true);  
  131.         Unsafe unsafe = (Unsafe)unsafeField.get(null);  
  132.         while(true){  
  133.             unsafe.allocateMemory(_1MB);  
  134.         }  
  135.     }  
  136.       
  137.       
  138.       
  139.       
  140.     public static void main(String[] args) {  
  141.         TestOOM oom = new TestOOM();  
  142. //      ---------测试堆内存溢出-----------  
  143. //      oom.testHeapOOM();    
  144.           
  145. //      ---------测试栈溢出----------  
  146. //      try{  
  147. //          oom.stackLeak();   
  148. //      }catch(Throwable error){  
  149. //          System.out.println("Stack length-->"+count);  
  150. //          throw error;  
  151. //      }  
  152.           
  153. //      ---------测试由于栈动态扩展导致的OOM----------        
  154. //      try{  
  155. //          oom.stackLeakByThread();  
  156. //      }catch(Throwable error){  
  157. //          System.out.println("Stack length-->"+count);  
  158. //          throw error;  
  159. //      }  
  160.           
  161. //      ----------测试方法区溢出----------  
  162. //      oom.methodAreaOOM();  
  163.           
  164. //      ----------测试常量池溢出----------  
  165. //      oom.constantPoolOOM();  
  166.           
  167. //      ----------测试直接内存溢出----------  
  168.           
  169.         try {  
  170.             oom.directMemoryOOM();  
  171.         } catch (Exception e) {  
  172.             System.out.println(e);  
  173.         }  
  174.           
  175.           
  176.           
  177.     }  
  178.       
  179.       
  180. }  

0 0
原创粉丝点击