JVM内存溢出分析-实战JVM(二)

JVM规范规定,除了程序计数器,虚拟机其余内存区域均会发生内存溢出的可能,OutOfMemoryError(OOM) java

原文地址:http://www.begincode.net/blog/62  个人网站,欢迎你们多提意见 eclipse

本文目的: jvm

    一、经过代码人为形成OOM,让你们跟了解JVM运行时各区存储的内容。 函数

    二、经过demo让你们实际开发过程当中,可以根据异常判断是那个内存区域发生的溢出, 工具

    三、让你们了解到什么样的代码会产生OOM,开发中可以尽可能规避。 测试

前提: 网站

     先和你们介绍一下eclipse如何设置JVM参数,Xms 最小堆内存,Xmx最大堆内存, spa

      HeapDumpOnOutOfMemoryError:发生内存溢出时生成堆转储快照 操作系统

1、JAVA堆内存溢出 .net

 代码实例,来自<<深刻理解JVM虚拟机>>

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

}

运行结果以下,发生内存溢出,在OutOfMemoryError后面会跟着 Java heap space 提示是堆内存溢出,

同时生成 java_pid10308.hprof 文件

该文件能够用分析工具MAT进行分析,安装连接http://www.begincode.net/blog/45 也能够用其余工具,

java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid10308.hprof ...
Heap dump file created [27883798 bytes in 0.497 secs]

经过工具能够分析是内存泄露仍是内存溢出,若是不存在泄露,那就是存在着不少对象实例没法回收,能够经过分析查看是那些对象,是否必须存活,若是必须存活在考虑适当调整JVM堆参数,如Xmx Xms等在之后的文章中会在此介绍。

2、虚拟机栈和本地方栈溢出(栈溢出)

    一、虚拟机栈

   虚拟机栈溢出,理论上分为两种,一种是线程请求的栈深度大于虚拟机容许的最大深度,另一种是申请的空间不足。

   由于虚拟机栈记录的是局部变量(方法变量)和函数调用栈针,则产生堆栈溢出通常是函数调用深度,如递归类或者

    方法A调用方法B,方法B调用方法C这样调用深度超过了虚拟机容许的最大深度,

    JVM参数设置为以下,Xss128k  Xss是堆栈大小

-Xms20m -Xmx20m -Xss128k -XX:+HeapDumpOnOutOfMemoryError

 demo以下

public class TestOOM {
	static int stackIndex = 0;
	public   void testStackOverFlow(){
		stackIndex++;
		testStackOverFlow();
	}

	public static void main(String[] args) throws Throwable  {
		try{
			TestOOM oom = new TestOOM();
			oom.testStackOverFlow();
		}catch(Throwable e){
			System.out.println("堆栈深度(迭代次数):"+stackIndex);
			throw e;
		}
	}
}

执行结果,堆栈深度达到983,堆栈溢出,并会给出具体是类,方法,发生的错误

堆栈深度(迭代次数):983
Exception in thread "main" java.lang.StackOverflowError
	at zgwx.TestOOM.testStackOverFlow(TestOOM.java:9)
	at zgwx.TestOOM.testStackOverFlow(TestOOM.java:10)
相同参数状况下若是修改递归的方法,在方法内建立几个变量你们再看下结果。
public   void testStackOverFlow(){
		stackIndex++;
		int str = 12;
		int str2 = 12;
		int str3 = 12;
		int str4 = 12;
		int str5 = 12;
		int str6 = 12;
		int str7 = 12;
		int str8 = 12;
		testStackOverFlow();
	}

执行结果以下,迭代次数明显减小,说明局部变量占用了堆栈的空间

堆栈深度(迭代次数):579
Exception in thread "main" java.lang.StackOverflowError
	at zgwx.TestOOM.testStackOverFlow(TestOOM.java:9)
	at zgwx.TestOOM.testStackOverFlow(TestOOM.java:18)

    二、本地方法栈,本地方法栈能够经过建立线程的方式来实现溢出,由于java线程是映射到操做系统内核上,因此线程会调用本地方法,形成本地方法栈的溢出

   本机环境没测试出来,机器卡死了,你们能够本身试试尽可能多建立线程,就会撑爆本地方法栈

   下面是找来的例子,没见到效果,都死卡死机了结

public class Test {
	private void runAlways(){
		while(true){
			
		}
	}
	public void createThread(){
		while(true){
			Thread thread = new Thread(new Runnable(){
				public void run() {
					runAlways();
				}
				
			});
			thread.start();
		}
	}
	public static void main(String[] args) {
		Test test = new Test();
		test.createThread();
	}
	}

3、直接本机内存溢出

    本机内存溢出能够无限制申请空间便可,用nio的申请缓冲区方式

public static void main(String[] args) {
		List list = new ArrayList();
		while(true){
			ByteBuffer byteBuffer = ByteBuffer.allocateDirect(1024*2048);
			list.add(byteBuffer);
		}
	}

执行结果以下,OutOfMemoryError后面的提示,Direct buffer memory 说明了直接缓冲区,

Exception in thread "main" java.lang.OutOfMemoryError: Direct buffer memory
	at java.nio.Bits.reserveMemory(Bits.java:658)
	at java.nio.DirectByteBuffer.<init>(DirectByteBuffer.java:123)

4、方法区(永久代)溢出

    方法区存储的是加载的类信息,常量,咱们就循环建立字符串常量,让方法区溢出

    为了尽快溢出,jvm参数设置永久代参数5M: -XX:PermSize=5m  -XX:MaxPermSize=5m

    代码以下:

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

 运行结果以下:OutOfMemoryError后面提示 PermGen space ;改代码在jdk6及如下版本可以报出以下异常,jdk7高版本及以上部分jvm已经逐渐开始取消永久代的缘由,只会报出堆内存异常

Exception in thread "main" java.lang.OutOfMemoryError: PermGen space
	at java.lang.String.intern(Native Method)
	at com.TestSocket.main(TestSocket.java:12)
相关文章
相关标签/搜索