Java内存区域

Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域,这些数据区域可以分为两个部分:一部分是线程共享的,一部分是线程私有的。其中,线程私有的数据区包括虚拟机栈、本地方法栈和程序计数器。线程共享的数据区具体包括Java堆和方法区两个区域。

程序计数器

线程是CPU调度的基本单位,多线程情况下,当线程数超过CPU数量或CPU内核数时,线程之间就要根据时间片轮询抢夺CPU时间资源。也就是说,在任何一个确定的时刻,一个处理器都只会执行一条线程中指令。因此,为了线程切换后能够恢复到正确的执行位置,每条线程都需要一个独立的程序计数器去记录其正在执行的字节码指令地址。程序计数器是线程私有的一块较小的内存空间,可以看做是当前线程所执行的字节码的行号指示器。

虚拟机栈

虚拟机栈描述的是Java方法执行的内存模型,是线程私有的。每个方法在执行的时候都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息,而且每个方法从调用直至完成的过程,对应一个栈帧在虚拟机栈中入栈到出栈的过程。其中,局部变量表主要存放一些基本类型的变量(int,short、long、byte、float、double、boolean、char)和对象句柄,他们可以是方法参数,亦可以是方法的局部变量。

本地方法栈

本地方法栈与Java虚拟机栈非常类似,也是线程私有的,区别是虚拟机栈为虚拟机执行Java方法服务,而本地方法栈为虚拟机执行Native方法服务。与虚拟机栈一样,本地方法栈区域也会抛出StackOverflowErrorOutOfMemoryError异常。

Java堆

Java堆的唯一目的就是存放对象实例,几乎所有的对象实例(和数组)都在这里分配内存。Java堆是线程共享的,类的对象从中分配空间,他们不需要程序代码来显示释放。Java堆是垃圾收集器管理的主要区域,因此很多时候也被称为“GC堆”,从内存回收角度看,由于现在的垃圾收集器基本都采用分代收集算法,所以为了方便垃圾回收Java堆还可以分为新生代和老年代。Java堆在实现时,既可以时固定大小的,也可以是可扩展的。如果在堆中没有内存完成实例分配,并且堆也无法再拓展时,会抛出OutOfMemoryError异常。

方法区

方法区与Java堆一样,也是线程共享的并且不需要连续的内存,其用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。方法区又包含常量池:常量池用于存放编译期生成的各种字面量和符号引用。其中,字面量比较接近Java语言层次的常量概念,如文本字符串、被声明为final的常量值等;而符号引用则属于编译原理方面的概念,包括以下三类常量:类和接口的全限定名、字段的名称和描述符和方法的名称和描述符。