Java堆栈溢出小记
今天,有人问我如何编写Java代码并在运行时抛出堆栈溢出异常。 看起来很简单的问题与Java虚拟机知识相关,特别是在本文中。
Java虚拟机结构概述
根据《Java虚拟机规范》 (thejavavirtualmachinespecification )对Java虚拟机运行时数据区(运行时数据区)的描述,虚拟机运行时的描述及其结构图显示在
图中,PC寄存器、Java虚拟机堆栈和本地方法堆栈是每个线程的专用,方法区域(包括运行时常量提取)和堆是线程之间共享的内存空间。 问题的堆栈溢出涉及两个区域,包括Java虚拟机堆栈和本地方法堆栈。 参见《Java虚拟机规范》,对堆栈溢出有以下两种说明:
对于Java虚拟机堆栈
对于本地方法堆栈
由此可见,Java虚拟机堆栈和本地方法堆栈都定义了两种类似的溢出。 这意味着线程为堆栈分配内存时内存不足。 线程递归调用方法时通常会发生此溢出。 当线程调用方法时,虚拟机会创建堆栈帧以保存方法调用信息,并在方法调用完成后销毁堆栈帧以释放存储空间。 如果在方法调用过程中无法创建堆栈帧,则会报告堆栈溢出错误异常。
无法在创建动态扩展堆栈或线程时分配足够的内存。 创建新线程时通常会发生此溢出。 要创建新线程,必须为堆栈分配存储。 如果堆栈没有足够的存储分配,则会报告内存错误退出异常。
代码实现
以下代码在Mac版JDK8中实现并执行。 由于HotSpot实现不分为Java虚拟机堆栈和本地方法堆栈,因此以下代码仅适用于Java虚拟机堆栈。 在Hotspot中设置堆栈容量的参数为-Xss,在后续的实验中全部设置为-Xss1M,并在Junit4中进行了测试
分配堆栈帧失败(堆栈溢出错误)
代码:
最终抛出java.lang.StackOverflowError,最终可达到的堆栈深度主要涉及堆栈内存的最大大小和堆栈帧中本地变量占用的空间。 使用以下代码时,最大深度将大大减小
堆栈上的内存分配给线程失败(OutOfMemoryError ) ) ) ) ) ) )。
代码:
最终抛出内存输出错误。
对内存错误的补充
在HotSpot虚拟机实现中,Java线程创建映射到操作系统线程,如果无法创建操作系统线程,则会抛出异常。 具体为: Java.lang.out of memory error 3360 unabletocreatenewnativethread。
对于通过实验的MacOS,通常小于2048。 在多次测试中为2023 )。 因为默认的Mac每个进程的最大分配线程数为2048。 可以使用sysctl kern.num_taskthreads命令运行查询。 如果需要突破限制,请参阅官方解决方案。
在centOS上的实验中,发现max_user_processes和堆栈大小参数限制了每个进程的线程数。
参考文献
[1] wmdxbw .深入了解Java虚拟机[M] .北京:机械工业出版社,53