zoukankan      html  css  js  c++  java
  • JVM内存溢出的方式

    了解了Java虚拟机五个内存区域的作用后,下面我们来继续学习下在什么情况下

    这些区域会发生溢出。

    1.虚拟机参数配置

    -Xms:初始堆大小,默认为物理内存的1/64(<1GB);默认(MinHeapFreeRatio参数可以调整)空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制。

    -Xmx:最大堆大小,默认(MaxHeapFreeRatio参数可以调整)空余堆内存大于70%时,JVM会减少堆直到 -Xms的最小限制。

    -Xss:每个线程的堆栈大小。JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K。应根据应用的线程所需内存大小进行适当调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。一般小的应用, 如果栈不是很深, 应该是128k够用的,大的应用建议使用256k。这个选项对性能影响比较大,需要严格的测试。

    -XX:PermSize:设置永久代(perm gen)初始值。默认值为物理内存的1/64。

    -XX:MaxPermSize:设置持久代最大值。物理内存的1/4。


    2.方法区溢出

    因为方法区是保存类的相关信息的,所以当我们加载过多的类时就会导致方法区
    溢出。在这里我们通过JDK动态代理和CGLIB代理两种方式来试图使方法区溢出。

    2.1 JDK动态代理

    package com.cdai.jvm.overflow;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class MethodAreaOverflow {
    
    	static interface OOMInterface {
    	}
    	
    	static class OOMObject implements OOMInterface {
    	}
    	
    	static class OOMObject2 implements OOMInterface {
    	}
    	
    	public static void main(String[] args) {
    		final OOMObject object = new OOMObject();
    		while (true) {
    			OOMInterface proxy = (OOMInterface) Proxy.newProxyInstance(
    					Thread.currentThread().getContextClassLoader(), 
    					OOMObject.class.getInterfaces(), 
    					new InvocationHandler() {
    						@Override
    						public Object invoke(Object proxy, Method method, Object[] args)
    								throws Throwable {
    							System.out.println("Interceptor1 is working");
    							return method.invoke(object, args);
    						}
    					}
    			);
    			System.out.println(proxy.getClass());
    			System.out.println("Proxy1: " + proxy);
    			
    			OOMInterface proxy2 = (OOMInterface) Proxy.newProxyInstance(
    					Thread.currentThread().getContextClassLoader(), 
    					OOMObject.class.getInterfaces(), 
    					new InvocationHandler() {
    						@Override
    						public Object invoke(Object proxy, Method method, Object[] args)
    								throws Throwable {
    							System.out.println("Interceptor2 is working");
    							return method.invoke(object, args);
    						}
    					}
    			);
    			System.out.println(proxy2.getClass());
    			System.out.println("Proxy2: " + proxy2);
    		}
    	}
    
    }
    虽然我们不断调用Proxy.newInstance()方法来创建代理类,但是JVM并没有内存溢出。
    每次调用都生成了不同的代理类实例,但是代理类的Class对象没有改变。是不是Proxy
    类对代理类的Class对象有缓存?具体原因会在之后的《JDK动态代理与CGLIB》中进行
    详细分析。

    2.2 CGLIB代理

    CGLIB同样会缓存代理类的Class对象,但是我们可以通过配置让它不缓存Class对象,
    这样就可以通过反复创建代理类达到使方法区溢出的目的。
    package com.cdai.jvm.overflow;
    
    import java.lang.reflect.Method;
    
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    public class MethodAreaOverflow2 {
    
    	static class OOMObject {
    	}
    
    	public static void main(String[] args) {
    		while (true) {
    			Enhancer enhancer = new Enhancer();
    			enhancer.setSuperclass(OOMObject.class);
    			enhancer.setUseCache(false);
    			enhancer.setCallback(new MethodInterceptor() {
    				@Override
    				public Object intercept(Object obj, Method method,
    						Object[] args, MethodProxy proxy) throws Throwable {
    					return method.invoke(obj, args);
    				}
    			});
    			OOMObject proxy = (OOMObject) enhancer.create();
    			System.out.println(proxy.getClass());
    		}
    	}
    	
    }


    3.堆溢出

    堆溢出比较简单,只需通过创建一个大数组对象来申请一块比较大的内存,就可以使
    堆发生溢出。
    package com.cdai.jvm.overflow;
    
    public class HeapOverflow {
    
    	private static final int MB = 1024 * 1024;
    	
    	@SuppressWarnings("unused")
    	public static void main(String[] args) {
    		byte[] bigMemory = new byte[1024 * MB];
    	}
    
    }


    4.栈溢出

    栈溢出也比较常见,有时我们编写的递归调用没有正确的终止条件时,就会使方法不断
    递归,栈的深度不断增大,最终发生栈溢出。
    package com.cdai.jvm.overflow;
    
    public class StackOverflow {
    
    	private static int stackDepth = 1;
    	
    	public static void stackOverflow() {
    		stackDepth++;
    		stackOverflow();
    	}
    	
    	public static void main(String[] args) {
    		try {
    			stackOverflow();
    		} 
    		catch (Exception e) {
    			System.err.println("Stack depth: " + stackDepth);
    			e.printStackTrace();
    		}
    	}
    	
    }

  • 相关阅读:
    链表
    std::map
    linux配置永久ip不生效解决方法
    linux命令英文缩写的含义(方便记忆)
    互联网创业其实就这24种商业模式
    当用反射获取一个model,这个model里面字段有nullable的时候,获取字段真实类型
    自定义alert和confirm
    web上传大文件的配置
    windows服务-log4net的使用
    vs中的各种快捷键
  • 原文地址:https://www.cnblogs.com/xiaomaohai/p/6157810.html
Copyright © 2011-2022 走看看