Java内存区域与内存溢出异常
Java虚拟机在执行Java程序的过程中会把它管理的内存分为不同的若干个数据区域。
具体的情况如下图所示:

jvm
我们可以可到运行时数据区主要有以下几个部分组成:
- 程序计数器(Program Counter Register)
- Java虚拟机栈(VM Stack)
- 本地方法栈(Native Method Stack)
- Java堆(Heap)
- 方法区
我们一个个看分析一下这些部分的功能和特点。
程序计数器(Program Count Register)
程序计数器是一块比较小的内存区域,它的作用是:当前线程所助兴的字节码的符号显示器。
这里有一个需要注意的地方,即【当前线程】,也就是说程序计数器是每个线程独享的,它用于在线程切换后能恢复到上一次的正确执行位置。
这个区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。
Java虚拟机栈
Java虚拟机栈也是线程私有的,它的生命周期与线程相同。
这一部分描述的是Java方法执行的内存模型:
每个方法被执行的时候都会创建一个栈帧(Stack Frame),用于存储局部变量表等信息。每一个方法被调用直至执行完成的过程,就对应着一个栈帧从入栈到出栈的过程。
在虚拟机栈中最重要的部分是局部变量表, 存放了编译期可知的基本数据类型,对象引用等等。
局部变量表所需的内存空间在编译期完成分配。
这个区域可能抛出两种异常:
- 如果线程请求的栈深度大于虚拟机所允许的深度: 抛出StackOverflowError
- 如果虚拟机栈可以动态扩展,当扩展无法申请到足够内存时,抛出OutOfMemoryError
本地方法栈
本地方法栈与虚拟机栈基本一致,不同的是本地方法栈是位虚拟机使用到的Native方法服务的。在有些虚拟机中(Sun HotSpot),直接把本地方法栈和虚拟机栈合二为一了。
Java堆
我们尝试总结Java堆的特性如下:
- Java堆是被所有线程共享的一块内存区域
- 在虚拟机启动时创建
- 这一区域的目的是:存放对象实例
- Java堆是垃圾收集器管理的主要区域,因此很多时候也被称为“GC堆”
- Java堆可以处于物理上不连续的内存空间,只要在逻辑上连续就行了
方法区
与Java堆一样,方法区也是线程共享的内存区域。
它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等等。
垃圾收集行为在这个区域比较少见,回收目标主要是常量池的回收和对类型的卸载。
运行时常量池(Runtime Constant Pool)
运行时常量池是方法区的一部分,用于存放编译器生成的各种字面量和符号引用。
运行时常量池具备动态性,运行期间也可以将新的常量放入池中。