以Java代码的方式总结几个典型的内存溢出案例
一、图示
我们先来看看今天要介绍哪些内存溢出案例,这里总结了一张图,如下所示。
二、定义主类结构
首先,我们创建一个名称为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); } } }
到此这篇关于以Java代码的方式列举几个典型的内存溢出案例总结的文章就介绍到这了,更多相关Java内存溢出内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!
赞 (0)