JAVA内存区域中不同的结构会由于不同的原因而导致内存溢出。JAVA内存主要分为堆,栈,方法区和程序计数器四个部分。程序计数器是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。其他三个区域都有可能发生内存溢出。下面我们来具体说说。
对于内存溢出(Memory Overflow),还有一个相似的概念就是内存泄露(Memory Leak)。它们有着本质的不同,内存泄露会导致内存溢出。内存泄露是没有必要存活的对象应该被GC回收,然而还有引用指向对象导致GC不能回收,这一般是程序的问题。
一 堆溢出
堆用来存储对象实例,只要不断的创建对象,并且保证GC Roots到对象之间有可达路径来避免垃圾回收机制清除这些对象,则在对象数量到达最大堆的容量限制后会产生内存溢出异常。内存溢出在堆中就会导致OOM。下面的例子在需要设定虚拟机的参数:-Xmx(堆的最大值)和 -Xms(堆的初始值)。下图显示异常位置:Java heap space
二 栈溢出
栈溢出有两种异常:
1.如果线程请求的栈深度大于虚拟机所允许的最大深度,则将抛出*Error异常。
2.如果虚拟机在扩展栈时无法申请到做够的内存空间,就会抛出OutOfMemoryError异常。
上面两种情况存在着相互重叠,本质是对同一件事情的两种描述而已。可以创建很多个栈帧来让它溢出,其实就是定义一个递归方法,让栈帧占满栈空间,这会导致*Error异常,栈深度一般在1000到2000。
创建很多个线程可以使虚拟机抛出OutOfMemoryError,由于Java线程是映射到操作系统的内核上,因此在做这个实验时很容易导致死机,我试验过一次,电脑就卡死了。下面就展示抛出的第一个异常: