· StackOverflowError与OutOfMemoryError是JVM中常见的有关内存的异常,需结合JVM来;
· 在理解、区分这两个异常前,需要知道JVM中运行时数据区的结构;【见图1的讲解】
· 在了解JVM中运行时数据区的结构后,才能对以上两个异常从内存与线程层面有个好的认识;【见图2的讲解】
一、前置知识:JVM运行时数据区
见图1,JVM运行时,数据区主要有5大类信息:方法区(JDK 8之后叫做元空间)、JVM栈、本地方法栈、堆、PC寄存器。
· 方法区(元空间):
在Java虚拟机中,元空间(方法区)是可提供各个线程共享的运行时内存区域,它存储了每一个类的结构信息,例如运行时常量池,字段和方法数据,构造函数和普通函数的字节码内容。它利用的是本地内存,不是JVM内存。
· JVM栈:
虚拟机栈也是每条线程私有的区域,里头存储栈帧(Frame),后面会重点介绍栈帧算是重点内容。方法的调用与返回基于栈帧来实现的。
· 本地方法栈:
在Java中调用别的语言代码(例如C/C++)的话就需要用到别的方法栈。JVM会用到传统的栈(C stack)来调用native方法,这个就是本地方法栈的应用,当然这个不是必须实现的,完全取决于虚拟机的实现。
· 堆:
在Java虚拟机中堆是所有线程都可以共享的内存区域,是存放所有类实例和数组对象的地方。在虚拟机启动就根据相关堆参数,创建堆,他也是GC工作的主要区域。
堆分为年轻代和老年代。
· PC寄存器:
全名叫做 Program Counter Register
,概念类比于计算机组成原理中的PC寄存器,存放下一条指令的地址,线程私有,方便线程切换(比如并发),用于保存现场。
二、StackOverflowError与OutOfMemoryError的结构
见图2,在JVM运行时数据区中:
· 方法区(JDK 8之后叫做元空间)、堆:是线程共享的,所有线程共用元空间和堆。堆用的是JVM内存,元空间使用本地内存,GC会对这两个部分进行收集。当堆或元空间满溢时,报错:OOM(OutOfMemoryError)
。
· JVM栈、本地方法栈、、PC寄存器:是线程私有的,每个线程都有属于自己的JVM栈、本地方法栈、PC寄存器。对于栈结构来说,只存在入栈、出栈操作,效率比使用内存结构高,GC不会对栈结构进行收集。(所以我们优化代码时可以利用逃逸分析原理来使得数据存储在JVM栈中。)一旦栈过大,超出范围,就会报错:StackOverflowError
。
对于PC寄存器来说,它只存一个地址,不存在溢出,也不会被GC收集。