JVM---2.内存溢出

三,内存溢出异常

    1.堆溢出:这个是最容易溢出的,实例化对象,无法获得空间存放。

设置jvm的堆空间大小:堆初始化大小20M,最大20M,新生代10M,打印打击回收详细信息,设置新生代中的比例为8:2

注意一定要设置-Xmx的堆最大值,不然它会扩大堆的大小,

测试代码:

public class MyT {
	static class OOMObject{
		
	}
	public static void main(String []args) {
		List<OOMObject> list=new ArrayList<>();
		while(true) {
			list.add(new OOMObject());
		}
	}
}

实验结果:显示发送oom,发生在heap space中。

(堆的内存有自动扩展功能)使用内存映像分析工具对堆的内存进行快照,然后提出出来分析,分析的目的是判断:堆中存货的对象是必须的还是垃圾。

    如果是垃圾就是:内存遗漏,分析gc为什么不进行回收。

    如果是必要的,则检查堆参数,看是否可以进行内存上调。或者代码的存储结构设计不合理,(比如疯狂递归等等)

    2.栈溢出

    包括虚拟机栈和本地方法栈溢出, -Xoss参数设置本地方法栈大小无效果,因为hotSpot不区分虚拟机栈和本地方法栈,栈容量由-Xss参数决定,

    异常抛出:1.线程请求的深度>虚拟机运行的max深度。抛出StackOverflowError异常

                      2.栈动态扩展申请不了空间。hotSpot不支持扩展。比如说,定义了大量的本地变量(局部变量)存放如栈中。或者又是递归调用。 抛出OutOfMemoryError异常

参数设置:

测试代码:   第一种异常情况,栈深度不够

public class MyT {
	private int stackLength =-1;
	public void stackLeak() {
		stackLength++;
		stackLeak();  //递归调用
	}
	public static void main(String []args) {
		MyT my=new MyT();
		try {
			my.stackLeak();
		}catch(Throwable e) {
			System.out.println("stack length: "+my.stackLength);
			throw e;
		}
	}
}

结果:

测试代码:第二种情况,局部变量表太大,没有空间可以满足分配

当我们设置栈的大小为64k方便实验的时候,jvm提示:The stack size specified is too small, Specify at least 108k,最小也要保持108k,所以我们选择了108k测试

public class MyT {
	private int stackLength =-1;
	public void stackLeak() {
		long u1=0,u2=0,u3=0,u4=0,u5=0,u6=0,u7=0,u9=0,u8=0,u11=0,u12=0,u13=0,u14=0,u15=0,u16=0,u17=0,u18=0,u19=0;
		long u1z=0,u2z=0,u3z=0,u4z=0,u5z=0,uz6=0,u7z=0,u9z=0,uz8=0,u1z1=0,u1z2=0,u1z3=0,uz14=0,uz15=0,u1z6=0,u17z=0,u18z=0,u1z9=0;
		long ua1=0,ua2=0,ua3=0,ua4=0,ua5=0,ua6=0,u7a=0,u9a=0,u8a=0,u11a=0,u12a=0,u13a=0,u14a=0,u1a5=0,u1a6=0,u1a7=0,u1a8=0,ua19=0;
		long uq1=0,uq2=0,u3q=0,uq4=0,uq5=0,u6q=0,u7q=0,u9q=0,uq8=0,u1q1=0,u1q2=0,u1q3=0,u1q4=0,u1q5=0,u16q=0,u1q7=0,u1q8=0,u1q9=0;
		long uw1=0,uw2=0,uw3=0,u4w=0,u5w=0,uw6=0,wu7=0,uw9=0,uw8=0,uw11=0,uw12=0,u1w3=0,uw14=0,uw15=0,u1w6=0,u1w7=0,u1w8=0,uw19=0;
		stackLength++;
		stackLeak();  //递归调用
	}
	public static void main(String []args) {
		MyT my=new MyT();
		try {
			my.stackLeak();
		}catch(Throwable e) {
			System.out.println("stack length: "+my.stackLength);
			throw e;
		}
	}
}

实验发现,不管是栈容量太小,还是单个栈帧太大,抛出的都是sof异常,

第三种情况,线程数量太多,导致unable to create native thread,此时就会抛出oom异常

  解决该异常的方法:原理是操作系统分配给进程的内存空间有限,当这个空间 - 最大堆内存 - 最大方法区内存 - 直接内存  - 虚拟机进程自身消耗的内存,程序计数器消耗很小,忽略不计,剩下的内存 就交给 虚拟机栈和本地方法栈来分配,如果一个栈的容量越小,那么可以分配的线程数量就越多,所以当发生因为线程分配导致的oom的时候,如果不能减少线程数量,那么就可以通过减少堆内存,或者减少栈容量来换取更多的线程。(注意,栈是私有的,所以一个栈就对应一个线程,-Xss也就是线程栈的容量,而不是总容量)

    3.方法区和运行时常量池溢出:由于jdk8使用元空间来代替永久代,这里就要讨论一下,两者的区别:

在jdk7后,常量池转移到了堆中,使用字符串的intern()可以想常量池添加内容,死循环添加就可以溢出。

设置堆大小,10M,注意最大值一定要设置

测试代码:

public class MyT {
	public static void main(String []args) {
		List<String> list=new ArrayList<>();
		int i=0;
		while(true) {
			list.add(String.valueOf(i++).intern());
		}
	}
}

结果:

     方法区的溢出则是,动态代理之类的,产生很多的类进行填满,spring等框架就经常使用动态代理,设置元空间大小:

设置参数:

public class MyT {
	public static void main(String []args) {
		while(true) {
			Enhancer enhancer=new Enhancer();
			enhancer.setSuperclass(myObject.class);
			enhancer.setUseCache(false);
			enhancer.setCallback(new MethodInterceptor() {
				public Object intercept(Object obj,Method method,Object[] args,MethodProxy proxy)throws Throwable{
					return proxy.invokeSuper(obj, args);
				}
			});
			enhancer.create();
		}
	}
	static class myObject{
	}
}

   

4.本机 直接内存 溢出:它的溢出不会产生很明显的东西,判断自己是否使用了nio。而其他溢出异常有没有发生。

测试代码:

	public static void main(String []args) {
		Field unsafeField =Unsafe.class.getDeclaredFields()[0];
		unsafeField.setAccessible(true);
		Unsafe unsafe=(Unsafe)unsafeField.get(null);
		while(true) {
			 unsafe.allocateMemory(1024*1024);
		}
	}

四,性能调优

1.堆  中间为m

   -Xms:初始化堆大小   

   -Xmx:最大堆大小

   -Xmn:年轻代大小

   -XX:NewRatio=n :设置年轻代和老年代的比值

   -XX:SurvivorRatio=n 年轻代中Eden区和两个Survivor区的比值,注意Survivor有两个.

2.栈   中间为s

   -Xss  设置每个线程的栈大小

3.元数据区设置

   -XX:MetaspaceSize   元数据区的初始化大小

   -XX:MaxMetaspaceSize  元数据去的最大大小

4.异常设置

    -XX:+HeapDumpOnOutOfMemoryError  使得jvm在产生内存溢出时,自动生成堆内存快照

    -XX:HeapDumpPath  改变默认的堆内存快照生成路径

5.收集器设置

    -XX:+UseSerialGC:设置串行收集器

    -XX:+UseParallelGC:设置并行收集器

    -XX:+UseParallelOldGC:设置并行老年代收集器

    -XX:+UseConcMarkSweepGC:设置并发收集器

6.垃圾回收统计信息

   -XX:+PrintGC

   -XX:+PrintGCDetails

    -XX:+PrintGCTimeStamps

    -Xloggc:filename

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值