从几个sample来学习JAVA堆、方法区、JAVA栈和本地方法栈

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

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

1)Java堆

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

 

  1. public class HeapOOM { 
  2.      
  3.     static class OOMObject{} 
  4.  
  5.     /**
  6.      * @param args
  7.      */ 
  8.     public static void main(String[] args) { 
  9.         List<OOMObject> list = new ArrayList<OOMObject>(); 
  10.          
  11.         while(true){ 
  12.             list.add(new OOMObject()); 
  13.         } 
  14.     } 
  15.  
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来调节,类太多有可能撑爆永久带:

 

  1. public class MethodAreaOOM { 
  2.      
  3.     static class OOMOjbect{} 
  4.  
  5.     /**
  6.      * @param args
  7.      */ 
  8.     public static void main(String[] args) { 
  9.         // TODO Auto-generated method stub 
  10.         while(true){ 
  11.             Enhancer eh = new Enhancer(); 
  12.             eh.setSuperclass(OOMOjbect.class); 
  13.             eh.setUseCache(false); 
  14.             eh.setCallback(new MethodInterceptor(){ 
  15.  
  16.                 @Override 
  17.                 public Object intercept(Object arg0, Method arg1, 
  18.                         Object[] arg2, MethodProxy arg3) throws Throwable { 
  19.                     // TODO Auto-generated method stub 
  20.                     return arg3.invokeSuper(arg0, arg2); 
  21.                 } 
  22.                  
  23.             }); 
  24.             eh.create(); 
  25.         } 
  26.     } 
  27.  
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

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

 

  1. public class ConstantOOM { 
  2.  
  3.     /**
  4.      * @param args
  5.      */ 
  6.     public static void main(String[] args) { 
  7.         // TODO Auto-generated method stub 
  8.         List<String> list = new ArrayList<String>(); 
  9.         int i=0; 
  10.         while(true){ 
  11.             list.add(String.valueOf(i++).intern()); 
  12.         } 
  13.     } 
  14.  
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如下所示:

 

  1. package com.cutesource; 
  2.  
  3. public class StackOOM { 
  4.  
  5.     /**
  6.      * @param args
  7.      */ 
  8.      
  9.     private int stackLength = 1; 
  10.      
  11.     public void stackLeak(){ 
  12.         stackLength++; 
  13.         stackLeak(); 
  14.     } 
  15.      
  16.     public static void main(String[] args) throws Throwable{ 
  17.         // TODO Auto-generated method stub 
  18.         StackOOM oom = new StackOOM(); 
  19.         try{ 
  20.             oom.stackLeak(); 
  21.         }catch(Throwable err){ 
  22.             System.out.println("Stack length:" + oom.stackLength); 
  23.             throw err; 
  24.         } 
  25.          
  26.     } 
  27.  
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的方法调用。当然报这样的错很少见,一般只会出现无限循环的递归中,另外,线程太多也会占满栈区域:

 

  1. package com.cutesource; 
  2.  
  3. public class StackOOM { 
  4.  
  5.     /**
  6.      * @param args
  7.      */ 
  8.      
  9.     private int stackLength = 1; 
  10.      
  11.     private void dontStop(){ 
  12.         while(true){ 
  13.             try{Thread.sleep(1000);}catch(Exception err){} 
  14.         } 
  15.     } 
  16.      
  17.     public void stackLeakByThread(){ 
  18.         while(true){ 
  19.             Thread t = new Thread(new Runnable(){ 
  20.  
  21.                 @Override 
  22.                 public void run() { 
  23.                     // TODO Auto-generated method stub 
  24.                     dontStop(); 
  25.                 } 
  26.                  
  27.             }); 
  28.             t.start(); 
  29.             stackLength++; 
  30.         } 
  31.     } 
  32.      
  33.     public static void main(String[] args) throws Throwable{ 
  34.         // TODO Auto-generated method stub 
  35.         StackOOM oom = new StackOOM(); 
  36.         try{ 
  37.             oom.stackLeakByThread(); 
  38.         }catch(Throwable err){ 
  39.             System.out.println("Stack length:" + oom.stackLength); 
  40.             throw err; 
  41.         } 
  42.          
  43.     } 
  44.  
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栈和本地方法栈

posted @ 2015-06-14 11:24  hzhtracy  阅读(1778)  评论(0编辑  收藏  举报