5、内存管理机制---内存溢出(配置参数)

除了计数器外,其他的内存空间都可能发生OutOfMemoryError(OOM)错误

1、java堆内存溢出

java堆是用来储存实例、方法区、句柄,只要不断创建对象,在gc没及时清除这些对象时,就会达到最大堆的容量限制产生 内存溢出异常。

 

查看jvm分配到的内存:

System.out.println(Runtime.getRuntime().maxMemory()/1024/1024+"M");

我的是247M;

点击项目 run configurations修改:


 再运行 :50M;

Xms最小内存,Xmx最大内存,-Xms50M中间没有空格;

扫描二维码关注公众号,回复: 590360 查看本文章

cmd  输入jconsole可以查看vm的相关信息

通过上面的方法将jvm内存设置成15m,大量创建实例来造成内存溢出:

public class Test { 
	public static void main(String...s){
	List<Test> l = new ArrayList<Test>();
	while(true){
		System.out.println(Runtime.getRuntime().freeMemory()/1024/1024+"M");
		l.add(new Test());
	}
	}
}

异常输出:

[GC 13594K->10285K(15872K), 0.0017656 secs]

[Full GC 10285K->10285K(15872K), 0.0219027 secs]

[Full GC 10285K->9855K(15872K), 0.0290098 secs]

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space 

操作系统给每个进程都有最大内存限定,比如32位系统对j进程分配最大为2G,除去heap和Method Area(占大部分),程序计数器(很小),剩余部分给jvm栈和本地方法栈;线程是相互独立的,线程是在虚拟机栈中分配;

2、jvm栈和本地方法溢出

HotSpot虚拟机是将本地方法栈和虚拟机栈放在一起的,栈容量是由-Xss参数设置。相关的错误有:

1、StackOverflowError:线程请求的栈深度大于虚拟机所允许的最大深度;(栈的帧数超过规定长度)

2、OutOfMemoryError:同heap内存溢出,jvm无法请求更多内存;

设置-Xss128k

public class Test1 {
	private int s=1;
   public void addself(){
	   s++;
	   addself();
   }
	public static void main(String[] args) throws Throwable {
		Test1 t1= new Test1();
		try{
			t1.addself();
		}catch(Throwable e){     //这是错误,不能是Excption
			System.out.println(t1.s);
			throw e;
		}
	}
}

2288

Exception in thread "main" java.lang.StackOverflowError

发生 栈溢出  错误 ,这个错误是在应用程序递归太深而发生堆栈溢出,就是说这是jvm限制的最大递归方法深度;

改为-Xss2m

输出:81135 明显请求深度增加了;

3、方法区和运行时常量池内存溢出OOM:

       上一章中讲过,常量池是方法区的一部分,配置-XX:PermSize=1m  -XX:PermSize=1m 限制方法区的大小,从而也限制了常量池的大小;

       String.intern()是一个Native方法,它的作用是:如果常量池(常量池在方法区中)中已经包含String实例如"ok",则返回实例"ok";

public static void main(String...s){
	int x=1;
	List<String > l= new 	ArrayList<>();
	while(true){
		l.add(String.valueOf(x++).intern());  
	}

上面这个常量池并不能在jdk1.7上造成OOM:PermGen  Space,在1.7以后的版本开始去“去永久代”,方法区和常量池 就是heap中的永久代,上章讲过;

str=new StringBuiler("计算机").append("编程").toString();

syso.(str.intern()==str);

在jdk1.6会出现false,intern方法会把首次遇到的字符串复制到常量池(永久代),然后再返回这个引用,而str是个heap内存(new 创建),所以他们是不同的引用,一个在堆,一个在永久代;所以是false,这个好理解;

而在jdk1.7中则出现true,说明他们同一个引用;在jdk1.7中,由于已经在heap中创建了一个内存,intern方法机制的不再去复制了,直接返回在堆内存中的引用若已有字符串如"java"在永久代中存在,intern返回就是永久代中的引用,如:

String str = new StringBuilder("ja").append("va").toString();

System.out.println(str.intern()==str); 

false;

可以用spring的通知或者直接用动态代理实现,无限制的while(true)增加通知,造成方法区溢出  OOM:PermGen  Space

参看java34章aop或者动态代理,这里不写代码了;

4、直接内存溢出

上章讲过,直接内存是堆外内存,通过-XX:MaxDirectMemorySize=10m来设置,如果不设置,则与java的最大heap内存一致(-Xmx设定)。

猜你喜欢

转载自nickfover.iteye.com/blog/2128334
今日推荐