Java内存区域划分

157 阅读3分钟

原文日期:2016-07-15


背景

在开发C/C++语言时,程序员需要对内存进行人工智能管理(手工),但如果程序员管理内存不到位,就容易造成内存泄漏和内存溢出等问题。然而在Java语言中,Java提供了一个内存管理机制和垃圾回收机制,这样Java程序员就不需要关心内存的分配和释放,降低内存泄漏和内存溢出等内存风险。

Java虚拟机规范对Java内存划分成不同的区域,以便Java虚拟机对内存管理,以下是Java内存区域划分示意图。 image.png

Java内存区域划分

如上图,是Java虚拟机对Java内存的区域划分。Java内存主要分两类,一类Java线程共享,另一类是Java线程私有。

程序计数器

程序计数器是记录了当前java线程执行字节码指令的地址信息,由于地址信息大小固定,故而它是Java唯一一个没有规定OutOfMemoryError内存溢出异常的区域。为什么需要设置这个区域呢?由于Java天生支持多线程并发,多线程并发执行是通过线程轮流切换并分配CPU时间片来实现,故而在线程切换时,需要将前一个线程执行的指令地址信息保存下来,以便后面线程切换时能够定位到正确的执行位置。

Java虚拟机栈

每个Java线程都有自己私有的Java虚拟机栈,Java虚拟机栈是伴随着线程的启动而产生,和线程有相同的生命周期,用于存储栈帧。栈帧是随着方法的调用执行而生成,方法执行完后进行销毁,每个方法的调用都会产生一个栈帧。栈帧包含方法的参数、局部变量、返回值等信息,在Java中主要分为如下几类:局部变量表、操作数栈、动态链接、方法返回地址。

局部变量表存储了当前方法的参数、局部变量和this引用地址。其中this引用地址在索引为0的位置上,这就是在每个方法中可以使用this标识来表明当前对象引用的原理,之后从1开始的索引是方法入参和局部变量。

操作数栈相当于C语言数据结构中的栈,数据的计算都是通过出入栈来完成。

Java虚拟机栈规定了两个异常情况,即StackOverflowError和OutOfMemoryError。Java虚拟机规范要求Java虚拟机栈要有一个最大和最小的容量,并可以交由开发人员配置。当Java虚拟机栈动态扩容的容量超过最大值时,则JVM会抛出StackOverflowError异常(可无限递归调用方法来模拟)。在动态扩容时,无法从JVM中申请分配内存时,JVM将抛出OutOfMemoryError异常。

本地方法栈

本地方法栈是针对native方法(调用其他语言的方法)创建的栈,其余特性和Java虚拟机栈一致。

堆是JVM很重要的内存区域,它存储了几乎所有的的类对象和数组对象。这些对象是受GC管理的,它们不需要也不能显示释放对象,该操作由GC内部机制处理。当堆的内存动态扩容,如果无法从JVM中分配到内存,JVM将抛出OutOfMemoryError异常。

方法区

方法区主要存储了类信息,常量,静态变量,代码。当类信息不断增大,导致方法区无法从JVM分配到可用内存,JVM将抛出OutOfMemoryError异常