7

【Java】几种典型的内存溢出案例,都在这儿了!

 3 years ago
source link: http://www.cnblogs.com/binghe001/p/12952772.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

写在前面

作为程序员,多多少少都会遇到一些内存溢出的场景,如果你还没遇到,说明你工作的年限可能比较短,或者你根本就是个假程序员!哈哈,开个玩笑。今天,我们就以Java代码的方式来列举几个典型的内存溢出案例,希望大家在日常工作中,尽量避免写这些low水平的代码。

定义主类结构

首先,我们创建一个名称为BlowUpJVM的类,之后所有的案例实验都是基于这个类进行。如下所示。

public class BlowUpJVM {  
}

栈深度溢出

public static void  testStackOverFlow(){ 
      BlowUpJVM.testStackOverFlow(); 
}

栈不断递归,而且没有处理,所以虚拟机栈就不断深入不断深入,栈深度就这样溢出了。

永久代内存溢出

public static void testPergemOutOfMemory1(){ 
   //方法一失败 
   List<String> list = new ArrayList<String>(); 
   while(true){ 
      list.add(UUID.randomUUID().toString().intern()); 
   } 
}

打算把String常量池堆满,没想到失败了,JDK1.7后常量池放到了堆里,也能进行垃圾回收了。

然后换种方式,使用cglib,用Class把老年代取堆满

public static void testPergemOutOfMemory2(){ 
   try { 
      while (true) { 
         Enhancer enhancer = new Enhancer(); 
         enhancer.setSuperclass(OOM.class); 
         enhancer.setUseCache(false); 
         enhancer.setCallback(new MethodInterceptor() { 
            @Override 
            public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { 
               return proxy.invokeSuper(obj, args); 
            } 
         }); 
         enhancer.create(); 
      } 
   } 
   catch (Exception e){ 
      e.printStackTrace(); 
   } 
}

虚拟机成功内存溢出了,那JDK动态代理产生的类能不能溢出呢?

public static void testPergemOutOfMemory3(){ 
   while(true){ 
   final OOM oom = new OOM(); 
   Proxy.newProxyInstance(oom.getClass().getClassLoader(), oom.getClass().getInterfaces(), new InvocationHandler() { 
         public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 
            Object result = method.invoke(oom, args); 
            return result; 
         } 
      }); 
   } 
}

事实表明,JDK动态代理差生的类不会造成内存溢出,原因是:JDK动态代理产生的类信息,不会放到永久代中,而是放在堆中。

本地方法栈溢出

public static void testNativeMethodOutOfMemory(){ 
   int j = 0; 
   while(true){ 
      Printer.println(j++); 
      ExecutorService executors = Executors.newFixedThreadPool(50); 
      int i=0; 
      while(i++<10){ 
         executors.submit(new Runnable() { 
            public void run() { 
            } 
         }); 
      } 
   } 
}

这个的原理就是不断创建线程池,而每个线程池都创建10个线程,这些线程池都是在本地方法区的,久而久之,本地方法区就溢出了。

JVM栈内存溢出

public static void testStackOutOfMemory(){ 
    while (true) {   
            Thread thread = new Thread(new Runnable() {   
                   public void run() { 
                          while(true){ 
                      } 
                   }   
            });   
            thread.start();   
     }   
}

线程的创建会直接在JVM栈中创建,但是本例子中,没看到内存溢出,主机先挂了,不是JVM挂了,真的是主机挂了,无论在mac还是在windows,都挂了。

温馨提示,这个真的会死机的。

堆溢出

public static void testOutOfHeapMemory(){ 
   List<StringBuffer> list = new ArrayList<StringBuffer>(); 
   while(true){ 
      StringBuffer B = new StringBuffer(); 
      for(int i = 0 ; i < 10000 ; i++){ 
         B.append(i); 
      } 
      list.add(B); 
   } 
}

不断往堆中塞新增的StringBuffer对象,堆满了就直接溢出了。

测试案例完整代码

public class BlowUpJVM {
    //栈深度溢出
    public static void  testStackOverFlow(){ 
      	BlowUpJVM.testStackOverFlow(); 
	} 
    
    //不能引起永久代溢出
    public static void testPergemOutOfMemory1(){ 
       //方法一失败 
        List<String> list = new ArrayList<String>(); 
       while(true){ 
          list.add(UUID.randomUUID().toString().intern()); 
       } 
    } 
    
    //永久代溢出
    public static void testPergemOutOfMemory2(){ 
       try { 
          while (true) { 
             Enhancer enhancer = new Enhancer(); 
             enhancer.setSuperclass(OOM.class); 
             enhancer.setUseCache(false); 
             enhancer.setCallback(new MethodInterceptor() { 
                @Override 
                public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { 
                   return proxy.invokeSuper(obj, args); 
                } 
             }); 
             enhancer.create(); 
          } 
       } 
       catch (Exception e){ 
          e.printStackTrace(); 
       } 
    } 
    
    //不会引起永久代溢出
    public static void testPergemOutOfMemory3(){ 
       while(true){ 
       final OOM oom = new OOM(); 
       Proxy.newProxyInstance(oom.getClass().getClassLoader(), oom.getClass().getInterfaces(), new InvocationHandler() { 
             public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 
                Object result = method.invoke(oom, args); 
                return result; 
             } 
          }); 
       } 
    } 
    
    //本地方法栈溢出
    public static void testNativeMethodOutOfMemory(){ 
       int j = 0; 
       while(true){ 
          Printer.println(j++); 
          ExecutorService executors = Executors.newFixedThreadPool(50); 
          int i=0; 
          while(i++<10){ 
             executors.submit(new Runnable() { 
                public void run() { 
                } 
             }); 
          } 
       } 
    } 
    
    //JVM内存溢出
    public static void testStackOutOfMemory(){ 
        while (true) {   
                Thread thread = new Thread(new Runnable() {   
                       public void run() { 
                              while(true){ 
                          } 
                       }   
                });   
                thread.start();   
         }   
    } 
    
    //堆溢出
    public static void testOutOfHeapMemory(){ 
       List<StringBuffer> list = new ArrayList<StringBuffer>(); 
       while(true){ 
          StringBuffer B = new StringBuffer(); 
          for(int i = 0 ; i < 10000 ; i++){ 
             B.append(i); 
          } 
          list.add(B); 
       } 
    } 
}

写在最后

如果觉得文章对你有点帮助,请微信搜索并关注「 冰河技术 」微信公众号,跟冰河学习高并发编程技术。

最后,附上并发编程需要掌握的核心技能知识图,祝大家在学习并发编程时,少走弯路。

Qze2AfR.jpg!web


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK