Java中堆溢出和栈溢出
Java虚拟机所管理的内存将会包括以下几个运行时数据区域
一、Java堆溢出
Java堆用于储存对象实例,当创建的对象过多,且对象没有被垃圾回收机制及时清除时,当容量达到堆的最大容量时就会产生内存溢出的问题
一般在运行Java项目时可通过参数:-XX: +HeapDumpOnOutOf-MemoryError,可以让虚拟机在出现内存溢出异常的时候Dump出当前的内存堆转储快照以便进行事后分析。
Java堆内存的OutOfMemoryError异常是实际应用中最常见的内存溢出异常情况。出现Java堆内存溢出时,异常堆栈信息“java.lang.OutOfMemoryError”会跟随进一步提示“Java heap space”。
导致OOM错误又分为内存泄漏(Memory Leak)和内存溢出(Memory
Overflow)
内存泄漏:程序中一些不再引用的对象,但由于某些原因(通常是因为对对象的引用未被正确释放),这些对象仍然被保留在内存中,无法被垃圾回收器回收 。导致内存越来越大,最终出现异常。
如果是内存泄漏,可进一步通过工具查看泄漏对象到GC Roots的引用链,找到泄漏对象是通过怎样的引用路径、与哪些GC Roots相关联,才导致垃圾收集器无法回收它们,根据泄漏对象的类型信息以及它到GC Roots引用链的信息,一般可以比较准确地定位到这些对象创建的位置,进而找出产生内存泄漏的代码的具体位置
内存溢出:程序在申请内存时,无法获得所需的内存空间,导致无法继续执行程序。
如果不是内存泄漏,换句话说就是内存中的对象确实都是必须存活的,那就应当检查Java虚拟机的堆参数(-Xmx与-Xms)设置,与机器的内存对比,看看是否还有向上调整的空间。再从代码上检查是否存在某些对象生命周期过长、持有状态时间过长、存储结构设计不合理等情况,尽量减少程序运行期的内存消耗。
二、虚拟机栈和本地方法栈溢出
由于HotSpot虚拟机中并不区分虚拟机栈和本地方法栈,因此对于HotSpot来说,-Xoss参数(设置本地方法栈大小)虽然存在,但实际上是没有任何效果的,栈容量只能由-Xss参数来设定。关于虚拟机栈和本地方法栈,在《Java虚拟机规范》中描述了两种异常:
1)如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError异常。
2)如果虚拟机的栈内存允许动态扩展,当扩展栈容量无法申请到足够的内存时,将抛出
OutOfMemoryError异常。
在多线程环境中,栈分配的内存越大,越容易出现内存泄漏,原因在于操作系统给每个进程分配的最大内存有一个限度。譬如32位Windows的单个进程
最大内存限制为2GB,HotSpot虚拟机提供了参数可以控制Java堆和方法区这两部分的内存的最大值。所以分配到栈的内存为,2GB减去最大堆内存,再减去最大方法去内存,,由于程序计数器消耗内存很小,可以忽略掉,如果把直接内存和虚拟机进程本身耗费的内存也去掉的话,剩下的内存就由虚拟机栈和本地方法栈来分配了。此时,如果每个线程的栈内存越大,可以创建线程的数量越少。