欢迎光临Software MyZone,有问题可留言或到站点论坛发帖,争取第一时间帮忙解决 || 站点论坛:火龙论坛 || 淘宝小店:应小心的易淘屋 【欢迎大家提建设性意见】

从几个sample来学习Java堆,方法区,Java栈和本地方法栈

欢迎热爱编程的朋友们参与到cocos2d-x编程中,为了给大家提供良好的交流环境,网站以开启QQ群
Software MyZone:66202765(群号,欢迎加入,若满,请加1群)
Software MyZone 1群(2dx):286504621
Software MyZone 2群(山东):204954191
Software MyZone【Java群】:162865493
【加群请写:Software MyZone或者是firedragonpzy】
淘宝店:【应小心的易淘屋】初次开店,大家多多支持……
群论坛:【火龙论坛】正试运营阶段,欢迎大家多提些建设性意见……
群问答:【火龙问答】正试运营阶段,欢迎大家多提些建设性意见……
摘自【有改动】:http://blog.csdn.net/cutesource/article/details/8244250

我的digitalocean推广链接:https://www.digitalocean.com/?refcode=65dfdb158f1a,Everyone you refer gets $10 in credit,从此链接注册,你将获得10美元。

最近在看《深入理解Java虚拟机》,书中给了几个例子,比较好的说明了几种OOM(OutOfMemory)产生的过程,大部分的程序员在写程序时不会太关注Java运行时数据区域的结构:

感觉有必要通过几个实在的例子来加深对这几个区域的了解

1)Java堆

所有对象的实例分配都在Java堆上分配内存,堆大小由-Xmx和-Xms来调节,sample如下所示:

public class HeapOOM {
	
	static class OOMObject{}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		List< oomobject > list = new ArrayList< oomobject >();
		
		while(true){
			list.add(new OOMObject());
		}
	}

}

加上JVM参数-verbose:gc -Xms10M -Xmx10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+HeapDumpOnOutOfMemoryError,就能很快报出OOM:

 

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

并且能自动生成Dump。

2)方法区

方法区是存放虚拟机加载类的相关信息,如类、静态变量和常量,大小由-XX:PermSize和-XX:MaxPermSize来调节,类太多有可能撑爆永久带:

public class MethodAreaOOM {
	
	static class OOMOjbect{}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		while(true){
			Enhancer eh = new Enhancer();
			eh.setSuperclass(OOMOjbect.class);
			eh.setUseCache(false);
			eh.setCallback(new MethodInterceptor(){

				@Override
				public Object intercept(Object arg0, Method arg1,
						Object[] arg2, MethodProxy arg3) throws Throwable {
					// TODO Auto-generated method stub
					return arg3.invokeSuper(arg0, arg2);
				}
				
			});
			eh.create();
		}
	}

}

加上永久带的JVM参数:-XX:PermSize=10M -XX:MaxPermSize=10M,运行后会报如下异常:

 

Exception in thread “main” java.lang.OutOfMemoryError: PermGen space

静态变量或常量也会有可能撑爆方法区:

public class ConstantOOM {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		List< string > list = new ArrayList< string >();
		int i=0;
		while(true){
			list.add(String.valueOf(i++).intern());
		}
	}

}

同样加上JVM参数:-XX:PermSize=10M -XX:MaxPermSize=10M,运行后报如下异常:

 

Exception in thread “main” java.lang.OutOfMemoryError: PermGen space

3)Java栈和本地方法栈

栈是存放线程调用方法时存储局部变量表,操作,方法出口等与方法执行相关的信息,栈大小由Xss来调节,方法调用层次太多会撑爆这个区域,samples如下所示:

package com.cutesource;

public class StackOOM {

	/**
	 * @param args
	 */
	
	private int stackLength = 1;
	
	public void stackLeak(){
		stackLength++;
		stackLeak();
	}
	
	public static void main(String[] args) throws Throwable{
		// TODO Auto-generated method stub
		StackOOM oom = new StackOOM();
		try{
			oom.stackLeak();
		}catch(Throwable err){
			System.out.println("Stack length:" + oom.stackLength);
			throw err;
		}
		
	}

}

设置JVM参数:-Xss128k,报出异常:

 

Exception in thread “main” java.lang.StackOverflowError

打印出Stack length:1007,这里可以看出,在我的机器上128k的栈容量能承载深度为1007的方法调用。当然报这样的错很少见,一般只会出现无限循环的递归中,另外,线程太多也会占满栈区域:

package com.cutesource;

public class StackOOM {

	/**
	 * @param args
	 */
	
	private int stackLength = 1;
	
	private void dontStop(){
		while(true){
			try{Thread.sleep(1000);}catch(Exception err){}
		}
	}
	
	public void stackLeakByThread(){
		while(true){
			Thread t = new Thread(new Runnable(){

				@Override
				public void run() {
					// TODO Auto-generated method stub
					dontStop();
				}
				
			});
			t.start();
			stackLength++;
		}
	}
	
	public static void main(String[] args) throws Throwable{
		// TODO Auto-generated method stub
		StackOOM oom = new StackOOM();
		try{
			oom.stackLeakByThread();
		}catch(Throwable err){
			System.out.println("Stack length:" + oom.stackLength);
			throw err;
		}
		
	}

}

报出异常:Exception in thread “main” java.lang.OutOfMemoryError:unable to create new native thread

 

不过在windows上运行这个例子要小心,会出现系统假死的情况,有可能需要重启机器才行。

以上几个例子虽然比较简单,但能很好帮助普通的程序员更加直观的了解Java堆方法区Java栈和本地方法栈

JVM环境参数怎么配置:

java -Xmx3550m -Xms3550m -Xmn2g -Xss128k

-Xmx3550m:设置JVM最大可用内存为3550M。

-Xms3550m:设置JVM促使内存为3550m。此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存。

-Xmn2g:设置年轻代大小为2G。整个堆大小=年轻代大小 + 年老代大小 + 持久代大小。持久代一般固定大小为64m,所以增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。

-Xss128k:设置每个线程的堆栈大小。JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K。更具应用的线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。

java -Xmx3550m -Xms3550m -Xss128k -XX:NewRatio=4 -XX:SurvivorRatio=4 -XX:MaxPermSize=16m -XX:MaxTenuringThreshold=0

-XX:NewRatio=4:设置年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代)。设置为4,则年轻代与年老代所占比值为1:4,年轻代占整个堆栈的1/5

-XX:SurvivorRatio=4:设置年轻代中Eden区与Survivor区的大小比值。设置为4,则两个Survivor区与一个Eden区的比值为2:4,一个Survivor区占整个年轻代的1/6

-XX:MaxPermSize=16m:设置持久代大小为16m。

-XX:MaxTenuringThreshold=0:设置垃圾最大年龄。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代。对于年老代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活时间,增加在年轻代即被回收的概论。

Tags: