运行时数据区
JVM 内存模型也可以称为 JVM 运行时数据区。其内部结构包括: 程序计数器, JAVA 堆,本地方法栈,虚拟机栈,方法区(包含运行时常量池) 。其中程序计数器,本地方法栈,虚拟机栈是线程私有空间, JAVA 堆,方法区是所有线程共享的数据区。
如图所示
程序计数器
程序计数器( Program Counter Register ) 是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。在虚拟机的概念模型李,字节码解释器工作时就是通过改变这个计数器的值来选取下一条所需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器完成。
每个线程都有自己独立的程序计数器,线程之间不会相互影响。这是为了线程切换后能恢复到正确的指定位置。
如果线程正在执行的是一个 JAVA 方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是 NATIVE 方法,这个计数器值则为空( Undefined )。此内存区域是唯一一个在 JAVA 虚拟机规范中没有规定任何 OutOfMemoryError 情况的区域
虚拟机栈
虚拟机栈( Java Virtual Machine Stacks ) 中包含的是 栈帧( Stack Frame ) ,每个栈帧对应一个被调用的方法。栈帧中包含局部变量表,操作数栈,执行运行时常量池的引用,方法返回地址,附加信息。
局部变量表存放了编译期可知的各种基本数据类型( boolean 、 type 、 char 、 short 、 int 、 float 、 long 、 double )对象引用( reference 类型)和 returnAddress 类型(指向了一条字节码指令的地址)
当线程执行一个方法时,就会创建一个对应的栈帧并压栈。方法执行完后就会将栈帧出栈。
与程序计数器一样, JAVA 虚拟机也是线程私有的,它的生命周期与线程相同。
本地方法栈
本地方法栈( Native Method Stack ) 与虚拟机栈所发挥的作用时非常相似的,它们之间的区别不过是虚拟机栈为方法服务,而本地方法栈则为虚拟机使用到的 Natvie 方法服务。在虚拟机规范中堆本地方法栈中方法使用的语言、使用方法与数据结构没有强制规定,因此具体的虚拟机可以自由实现它。
JAVA 堆
JAVA 堆( JAVA Heap ) 是用来存储对象本身和数组。堆是被所有线程共享的,在 JVM 中只有一个堆,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。
JAVA 堆是来及收集器管理的主要区域,因此很多时候也被称作 GC 堆( Garbage Collected Heap ) 。
根据 JAVA 虚拟机规范的规定, JAVA 堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可,
方法区
方法区( Method Area ) 和 JAVA 堆一样,是线程共享的。在方法区中存储了以被虚拟机加载的类信息(包括类的名称,方法信息,字段信息),静态变量,常量以及编译器编译后的代码等。
方法区中包含了 运行时常量池( Runtime Constant Pool ) ,运行时常量池中除了 Class 文件中定义的常量外,也支持运行期间加入新的常量。例如: String 的 intern ()方法,会检查运行时常量池中是否存在当前字符串,如果存在则返回引用地址,否则将字符串加入运行时常量池,然后返回地址。
参考文档:
《深入理解JAVA虚拟机》