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

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

原文地址:http://www.begincode.net/blog/62  我的网站,欢迎大家多提意见

本文目的:

1、通过代码人为造成OOM,让大家跟了解JVM运行时各区存储的内容。

2、通过demo让大家实际开发过程中,能够根据异常判断是那个内存区域发生的溢出,

3、让大家了解到什么样的代码会产生OOM,开发中能够尽量规避。

前提:

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

HeapDumpOnOutOfMemoryError:发生内存溢出时生成堆转储快照

一、JAVA堆内存溢出

代码实例,来自<<深入理解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等在以后的文章中会在此介绍。

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

1、虚拟机栈

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

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

方法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)

2、本地方法栈,本地方法栈可以通过创建线程的方式来实现溢出,因为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();
	}
	}

三、直接本机内存溢出

本机内存溢出可以无限制申请空间即可,用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)

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

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

为了尽快溢出,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)
时间: 2024-10-26 13:08:58

JVM内存溢出分析-实战JVM(二)的相关文章

BAT面试必问题系列:深入详解JVM 内存区域及内存溢出分析

前言 在JVM的管控下,Java程序员不再需要管理内存的分配与释放,这和在C和C++的世界是完全不一样的.所以,在JVM的帮助下,Java程序员很少会关注内存泄露和内存溢出的问题.但是,一旦JVM发生这些情况的时候,如果你不清楚JVM内存的内存管理机制是很难定位与解决问题的. 一.JVM 内存区域 Java虚拟机在运行时,会把内存空间分为若干个区域,根据<Java虚拟机规范(Java SE 7 版)>的规定,Java虚拟机所管理的内存区域分为如下部分:方法区.堆内存.虚拟机栈.本地方法栈.程序

关于JVM内存溢出的原因分析及解决方案探讨

前言:JVM中除了程序计数器,其他的区域都有可能会发生内存溢出. 0.什么是内存溢出 当程序需要申请内存的时候,由于没有足够的内存,此时就会抛出OutOfMemoryError,这就是内存溢出. 1.内存泄漏和内存溢出区别与联系 内存泄漏:系统分配的内存没有被回收. 内存溢出:分配的内存空间超过系统内存. 2.内存泄漏的原因分析   jvm由5大块组成:堆,栈,本地方法栈,程序计数器,方法区.栈它的主要记录方法的执行和对象的引用.堆则存在真正的引用的对象. 内存泄漏是由于使用不当,把一部分内存“

jvm虚拟机(一):jvm内存溢出问题的分析与解决

??学习一下java虚拟机系列,之一 添加运行参数-XX:+HeapDumpOnOutOfMemoryError -Xms30m -Xmx30m -XX:+HeapDumpOnOutOfMemoryError 这个参数会生成堆栈快照,用于定位异常 模拟内存溢出的场景,简单代码: 123456789101112131415161718192021222324252627282930313233 package top.alertcode.demo.jvm; import java.util.Arr

Tomcat中JVM内存溢出及合理配置

Tomcat本身不能直接在计算机上运行,需要依赖于硬件基础之上的操作系统和一个Java虚拟机.Tomcat的内存溢出本质就是JVM内存溢出,所以在本文开始时,应该先对Java JVM有关内存方面的知识进行详细介绍. 一.Java JVM内存介绍 JVM管理两种类型的内存,堆和非堆.按照官方的说法:"Java 虚拟机具有一个堆,堆是运行时数据区域,所有类实例和数组的内存均从此处分配.堆是在 Java 虚拟机启动时创建的.""在JVM中堆之外的内存称为非堆内存(Non-heap

JVM内存溢出及配置

一.Java JVM内存介绍 JVM管理两种类型的内存,堆和非堆.按照官方的说法:"Java 虚拟机具有一个堆,堆是运行时数据区域,所有类实例和数组的内存均从此处分配.堆是在 Java 虚拟机启动时创建的.""在JVM中堆之外的内存称为非堆内存(Non-heap memory)".简单来说堆就是Java代码可及的内存,是留给开发人员使用的:非堆就是JVM留给自己用的,所以方法区.JVM内部处理或优化所需的内存(如JIT编译后的代码缓存).每个类结构(如运行时常数池.

Tomcat中JVM内存溢出及合理配置(转)

Tomcat本身不能直接在计算机上运行,需要依赖于硬件基础之上的操作系统和一个Java虚拟机.Tomcat的内存溢出本质就是JVM内存溢出,所以在本文开始时,应该先对Java JVM有关内存方面的知识进行详细介绍. 一.Java JVM内存介绍 JVM管理两种类型的内存,堆和非堆.按照官方的说法:"Java 虚拟机具有一个堆,堆是运行时数据区域,所有类实例和数组的内存均从此处分配.堆是在 Java 虚拟机启动时创建的.""在JVM中堆之外的内存称为非堆内存(Non-heap

【转】Tomcat中JVM内存溢出及合理配置

Tomcat本身不能直接在计算机上运行,需要依赖于硬件基础之上的操作系统和一个Java虚拟机.Tomcat的内存溢出本质就是JVM内存溢出,所以在本文开始时,应该先对Java JVM有关内存方面的知识进行详细介绍. 一.Java JVM内存介绍 JVM管理两种类型的内存,堆和非堆. 按照官方的说法:“Java 虚拟机具有一个堆,堆是运行时数据区域,所有类实例和数组的内存均从此处分配.堆是在 Java 虚拟机启动时创建的.”“在JVM中堆之外的内存称为非堆内存(Non-heap memory)”.

5种JVM垃圾收集器特点和8种JVM内存溢出原因

先来看看5种JVM垃圾收集器特点 一.常见垃圾收集器 现在常见的垃圾收集器有如下几种: 新生代收集器: Serial ParNew Parallel Scavenge 老年代收集器: Serial Old CMS Parallel Old 堆内存垃圾收集器:G1 每种垃圾收集器之间有连线,表示他们可以搭配使用. 二.新生代垃圾收集器 (1)Serial 收集器 Serial 是一款用于新生代的单线程收集器,采用复制算法进行垃圾收集.Serial 进行垃圾收集时,不仅只用一条线程执行垃圾收集工作,

巧解Tomcat中JVM内存溢出问题

你对Tomcat 的JVM内存溢出问题的解决方法是否了解,这里和大家分享一下,相信本文介绍一定会让你有所收获. tomcat 的JVM内存溢出问题的解决 最近在熟悉一个开发了有几年的项目,需要把数据库从mysql移植到oracle,首先把jdbc的连接指向mysql,打包放到tomcat里面,可以跑起来,没有问题,可是当把jdbc连接指向oracle的时候,tomcat就连续抛java.lang.OutOfMemoryError的错误,上网google了一下,了解了一下tomcat的运行机制,也