写点什么

一文搞懂 5 种内存溢出案例,内含完整源码

  • 2024-06-05
    广东
  • 本文字数:3142 字

    阅读完需:约 10 分钟

一文搞懂5种内存溢出案例,内含完整源码

本文分享自华为云社区《10分钟搞懂各种内存溢出案例!!(含完整源码,建议收藏)》,作者:冰 河。


作为程序员,多多少少都会遇到一些内存溢出的场景,如果你还没遇到,说明你工作的年限可能比较短,或者你根本就是个假程序员!哈哈,开个玩笑。今天,我们就以 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);        }     } } 
复制代码


点击关注,第一时间了解华为云新鲜技术~

发布于: 刚刚阅读数: 5
用户头像

提供全面深入的云计算技术干货 2020-07-14 加入

生于云,长于云,让开发者成为决定性力量

评论

发布
暂无评论
一文搞懂5种内存溢出案例,内含完整源码_Java_华为云开发者联盟_InfoQ写作社区