JVM【第六回】:【OutOfMemoryError异常之虚拟机栈和本地方法栈溢出】

由于在HotSpot虚拟机中并不区分虚拟机栈和本地方法栈,因此对于HotSpot来说,-Xoss参数(设置本地方法栈大小)虽然存在,但实际上是无效的,栈容量只由-Xss参数设定。关于虚拟机栈和本地方法栈,在Java规范中描述了两种异常:

  • 如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError异常。
  • 如果虚拟机在扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常。

在下面的代码中,如果将范围限制于单线程中的操作,尝试下面两种方法均无法让虚拟机产生OutOfMemoryError异常,尝试的结果都是获得StackOverflowError异常,方法如下:

使用-Xss参数减少栈内存容量。结果抛出StackOverflowError异常,异常出现时输出的栈深度相应的缩小。

StackOverflowError异常代码如下:

package oom;

/**
 * 虚拟机栈和本地方法栈溢出
 * @author Madison
 * @date 2014-7-11
 * VM Args:-Xss128k
 */
public class JavaVMStackSOF 
{
	private int stackLength = 1;

	public void stackLeak()
	{
		stackLength++;
		stackLeak();
	}

	public static void main(String[] args) throws Throwable
	{
		JavaVMStackSOF oom = new JavaVMStackSOF();
		try
		{
			oom.stackLeak();
		}
		catch(Throwable e)
		{
			System.out.println("stack length:" + oom.stackLength);
			throw e;
		}
	}
}

运行结果:

stack length:2402

Exception in thread "main" java.lang.StackOverflowError

at oom.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:15)

at oom.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:15)

......后续异常栈信息省略

通过代码运行结果表明:在单个线程下,无论是由于栈帧太小,还是虚拟机栈容量太小,内存无法分配的时候,虚拟机抛出的都是StackOverflowError异常。

如果测试时不限于单线程,通过不断地建立线程的方式倒是可以产生内存溢出异常,但是,这样产生的内存溢出与栈空间是否足够大并不存在任何联系,或者准确地说,在这种情况下,给每个线程的栈分配的内存越大,反而容易产生内存溢出异常。

操作系统分配给每个进程的内存是有限制的。譬如32位Windows限制为2GB。虚拟机提供了参数来控制Java堆和方法区的这两部分内存的最大值。剩余的内存为2GB(操作系统限制)减去Xmx(最大堆容量),再减去MaxPermSize(最大方法区容量),程序计数器消耗的内存很小,可以忽略掉。如果虚拟机进程本身耗费的内存不计算在内,剩下的内存就由虚拟机栈和本地方法栈“瓜分”了。每个线程分配的栈容量越大,可以建立的线程数量自然就越少,建立线程时就越容易把剩下的内存耗尽。

这一点需要在开发多线程应用的时候特别注意,出现StackOverflowError异常时有错误堆栈可以阅读,相对来说,比较容易找到问题的所在。而且,如果是使用虚拟机默认参数,栈深度在大多数情况下达到1000~2000完全没有问题,对于正常的方法调用,这个深度应该完全够用了。但是,如果建立过多线程导致的内存溢出,在不减少线程数或者更换64位虚拟机的情况下,就只能通过减少最大堆和减少栈容量来换取更多的线程。如果没有这方面的经验,这种通过“减少内存”的手段来解决内存溢出的方式会比较难以想到。

创建线程导致内存溢出异常代码如下,运行该代码可能会导致操作系统假死。

package oom;

/**
 * 创建线程导致内存溢出
 * @author Madison
 * @date 2014-7-11
 * VM Args:-Xss2M
 */
public class JavaVMStackOOM 
{
	private void dontStop()
	{
		while(true)
		{

		}
	}

	public void stackLeakByThread()
	{
		while(true)
		{
			Thread thread = new Thread(new Runnable() 
			{
				@Override
				public void run() 
				{
					dontStop();
				}
			});
			thread.start();
		}
	}

	public static void main(String[] args) 
	{
		JavaVMStackOOM oom = new JavaVMStackOOM();
		oom.stackLeakByThread();
	}
}

运行结果:

Exception in thread "main" java.lang.OutOfMemoryError:unable to create new native thread

欲知后事如何,且听下回分解

JVM【第六回】:【OutOfMemoryError异常之虚拟机栈和本地方法栈溢出】

时间: 2024-10-10 06:23:51

JVM【第六回】:【OutOfMemoryError异常之虚拟机栈和本地方法栈溢出】的相关文章

Java虚拟机OOM之虚拟机栈和本地方法栈溢出(4)

一.在 Java 虚拟机规范中,对虚拟机栈这个区域规定了两种异常状况: (1)如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError 异常: (2)如果虚拟机栈可以动态扩展(当前大部分的 Java 虚拟机都可动态扩展,只不过 Java 虚拟机规范中也允许固定长度的虚拟机栈),当扩展时无法申请到足够的内存时会抛出 OutOfMemoryError 异常. (3)与虚拟机栈一样,本地方法栈区域也会抛出 StackOverflowError 和OutOfMemoryEr

第二章:Java虚拟机栈和本地方法栈溢出

由于在Hotspot虚拟机中中不区分虚拟机栈和本地方法栈,因此通过-Xoss修改参数是无效的,可以通过修改-Xss设定. 如果线程请求的栈深度大于虚拟机允许的最大深度,将抛出StackOverflowError异常. 如果虚拟机在扩展栈时无法申请到足够的内存空间,将抛出OutOfMemoryError异常. 这两种异常有一些重叠的部分:当栈空间无法继续分配时,到底是内存太小,还是已经使用的栈空间过大,其本质只是对同一件事情的两种不同描述. 可以通过一下方法验证: 在使用-Xss参数减少栈内存容量

Java虚拟机栈和本地方法栈

Java虚拟机栈的特征 线程私有 后进先出(LIFO)栈 存储栈帧,支持Java方法的调用.执行和退出 可能出现OutOfMemoryError异常和StackOverflowError异常 Java本地方法栈的特征 线程私有 后进先出(LIFO)栈 作用是支撑Native方法的调用.执行和退出 可能出现OutOfMemoryError异常和StackOverflowError异常 有一些虚拟机(如HotSpot)将Java虚拟机栈和本地方法栈合并实现 栈帧的概念和特征 Java虚拟机栈中存储的

JVM 运行时数据区:程序计数器、Java 虚拟机栈和本地方法栈,方法区和堆

Java 虚拟机可以看作一台抽象的计算机,如同真实的计算机,它也有自己的指令集和运行时内存区域. Java 虚拟机在执行 Java 程序的过程中会把它所管理的内存(运行时内存区域)划分为若干个不同的数据区域. 如下图所示: 一.程序计数器 Program Counter Register 定义:程序计数器是当前线程所执行字节码的行号指示器. 原因:Java 中的多线程是线程间轮流切换并需要 CPU 给予时间片的方式实现的.在任何一个确定的时刻,都只有一个线程在执行指令.为了线程间轮流切换后能够快

Jvm(11),运行时数据---独占区---本地方法栈

本地方法栈主要是来处理native的方法的,我们来看一下什么是native的方法. Java无法直接访问到操作系统底层(如系统硬件等),为此Java使用native方法来扩展Java 程序的功能. 其实在java中我们通用的虚拟机HotSpot中,本地方法栈和虚拟机栈是同一块区域在这里讲的一般是通用的虚拟机. 原文地址:https://www.cnblogs.com/qingruihappy/p/9691301.html

一起talk C栗子吧(第十六回:C语言实例--栈一)

各位看官们,大家好,从今天开始,我们讲大型章回体科技小说 :C栗子,也就是C语言实例.闲话休提, 言归正转.让我们一起talk C栗子吧! 看官们,上一回中咱们说的是双向链表的例子,这一回咱们说的例子是:栈. 什么是栈?我们听过龙门客栈,你这个是哪家客栈?我还没有说,台下已经有客官在问了.看官们,栈是 类似我们在前面几回中说过的链表,它也是用来存放数据的一种抽象的数据结构.因为比较抽象,咱们还 是举个现实生活中的例子来说明吧. 我们出去旅游时通常会拿一个行李箱存放自己的物品,比如衣服,鞋子电脑,

了解OutOfMemoryError异常 - 深入Java虚拟机读后总结

JVM中的异常发生 Java虚拟机规范中除了程序计数器外,其他几个运行时区域都有发生OutOfMemoryError异常的可能. 本章笔记通过代码来验证Java虚拟机规范中描述的各个运行时区域存储的内容.以及在以后遇到实际的内存溢出异常时,能根据异常的信息快速判断是哪个区域出现的内存溢出.怎样的代码可能会导致这些区域的内存溢出.以及这些问题该如何处理. Java堆溢出:Java堆用于存储对象实例,只要不断的创建对象,并且保证GC Roots到对象之间有可达路径避免垃圾回收机制清除对象,就会在对象

JVM --- OutOfMemoryError异常

在Java虚拟机规范的描述中,除了程序计数器外,虚拟机内存的其他几个运行时区域都有可能发生OutOfMemoryError(OOM)异常. 1.Java堆溢出 Java堆用于存储对象实例,只要不断地创建对象,并且保证GCRoots到对象之间有可大路径来避免垃圾回收机制清理这些对象,那么在对象数量到达最大堆的容量限制后就会产生内存溢出异常. 执行代码前提:Java堆大小为20MB,不可扩展(将堆的最小值-Xms参数与最大值-Xmx参数设置为一样即可避免堆自动扩展),通过参数-XX:+HeapDum

Java虚拟机OutOfMemoryError 异常详解及解决方法(3)

上图是一张Java运行时的内存分布图,可知虚拟机内存都有发生OutOfMemoryError(下文称 OOM)异常的可能,作为一个合格的Java开发人员,我们应该做到的是: (1)第一,通过代码验证 Java 虚拟机规范中描述的各个运行时区域储存的内容: (2)第二,遇到内存溢出的时候,应该可以找打具体的位置,并进行合理的解决: 下边就聊一下 OOM: 一.Java 堆溢出 我们知道Java 堆用于储存对象实例,我们只要不断地创建对象,并且保证 GC Roots 到对象之间有可达路径来避免垃圾回