
java虚拟机运行时数据区
其中蓝色的为线程共有的区域,红色为线程私有区域
1.程序计数器
它表示当前线程所执行的字节码的指令行。在虚拟机的概念模型里,字节码解释器通过改变计数器所指向的指令行,来选取下一个要执行的指令。因为在java虚拟机中多个线程来回切换执行对应的任务,为了能够切换后能够定位到线程之前程序的执行位置,所以每个线程都需要一个计数器来表示自己程序的执行情况,因此程序计数器对于线程来说是私有的。在执行java方法时,计数器当中会存储正在执行的虚拟机字节码的地址,但如果是native方法,那么计数器的值就会为空。程序计数器是唯一一个java虚拟机规范没有规定任何OutOfMemoryError情况的区域。
2.java虚拟机栈
它描述的是java方法执行的内存模型。也是线程私有的,虚拟机栈的生命周期和线程是一样的。在每次执行方法同时,会在虚拟机栈中创建一个栈帧,用于保存局部变量表、操作数栈、动态链接、方法出口等。每一个方法从调用直至执行完成的过程,就对应一个栈帧在虚拟机栈中入栈到出栈。
举个例子:
public class Obj {
public void m1() {
m2();
}
public void m2() {
System.out.println();
}
public static void main(string[] args) {
Obj obj = new Obj();
obj.m1();
}
}当main方法执行时,虚拟机栈将会发生如下操作:
准备执行m1,创建m1对应的栈帧,入栈;执行m1时,准备执行m2,创建m2对应的栈帧,入栈;m2执行完毕,m2对应的栈帧出栈;m1执行完毕,m1对应的栈帧出栈。如图:


其中存储的局部变量是在编译器可知的基本数据类型、对象引用(不等同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄或者其他能与此对象相关的位置)和returnAddress类型(指向一个字节码指令的地址,这个类型我还有点不太明白,我初步的想法是returnAddress可能是一种情况,在某个方法A中需要调用另外一个方法B,那么B的地址即为该字节码指令地址,如果有哪位大神知道真正的含义,希望告诉我一声^_^)。局部变量表所需的内存空间在编译期间就完成分配了,因此当进入一个方法时,这个方法需要在栈中分配多大的局部变量空间是完全确定的,在方法运行期间不会改变该大小。
在java虚拟机规范中,对这个区域规定两种异常情况:
- StackOverflowError异常:线程请求的栈深度大于虚拟机所允许的深度时。
- OutOfMemoryError异常:在虚拟机栈是可以动态扩展的情况下,当无法申请到足够的内存时。
3.本地方法栈
本地方法栈和虚拟机栈的作用是类似的,区别和他的名字一样,他是为本地方法(Native方法)服务的,在java虚拟机规范中,该区域也有两个异常:StackOverflowError和OutOfMemoryError。
4.java堆
java堆是多个线程所共享的区域。它的唯一目的就是存放对象实例(但说它是唯一一个存储对象的区域也不是“绝对”的)。java堆也被成为GC堆,是垃圾收集器管理的主要区域。也因此会对其区域再次划分为新生代老年代等等,不过不管怎么划分,堆存储的都是对象实例。
java虚拟机规定,java堆可以处于物理空间不连续的内存空间中,只要逻辑上是连续的就可以了。它实现时,即可以实现成固定大小的,也可以实现成可扩展的(通过-Xmx和-Xms控制)。如果说堆当中没有内存完成实例分配,并且堆也无法再扩展时,就会抛出OutOfMemoryError异常。
5.方法区
方法区和堆一样是线程所共有的区域。主要用于存储已被虚拟机加载的类信息、静态变量、常量、即时编译器编译后的代码等数据。
java虚拟机规范把方法区描述为堆的逻辑部分,也就是说方法区在某些方面和堆是一致的,比如说,它和堆一样不需要连续的内存,并且在实现时可以固定大小也可以选择可扩展。但它相对堆来说会更宽松一些,比如方法区可以选择不实现垃圾收集。但这并不代表该区域的数据会永久不回收,而是该区域的回收目标主要针对常量池的回收以及对类型的卸载。
根据java虚拟机规范规定,当方法区无法满足内存分配需求时,就会抛OutOfMemoryError异常。
6.运行时常量池
运行时常量池是方法区的一部分。它主要会加载两个时期的内容,一个是class文件中类的常量池数据(编译期生成的各种类的字面量和符号引用),会随着类加载后,进入到方法区的运行时常量池存放。另一个是在运行时期,也可能会有些新的常量被放入到运行时常量池中(比如开发人员利用String的intern()方法)。
运行时常量池做为方法去的一部分,自然受到方法区内存的限制,当常量池无法再申请到内存时会抛出OutOfMemoryError异常。