根据《Java 虚拟机规范(Java SE 8 版)》中,Java 运行时数据区主要分为:
Java 虚拟机可以支持多条线程同时执行,每一个 Java 虚拟机线程都有自己的 PC 寄存器(Program Counter Register)。
在任意时刻,一个 Java 虚拟机线程只会执行一个方法的代码,这个正在被线程执行的方法称为当前方法。如果当前方法不是 Native 方法,PC 寄存器就保存 Java 虚拟机正在执行的字节码指令的地址。如果当前方法时 Native 方法,PC 寄存器的值是 undefined。
每一个 Java 虚拟机线程都有自己私有的 Java 虚拟机栈(Java Virtual Machine Stack),这个栈与线程同时创建,用于存储栈帧(Frame)。
栈帧随着方法的调用而创建,随着方法的结束而销毁。栈帧用于存储局部变量表、操作数栈、动态链接和方法出口等信息。
如果线程请求分配的栈容量超过了 Java 虚拟机允许的最大容量,Java 虚拟机将会抛出一个 StackOverflowError
异常。如果 Java 虚拟机栈可以动态扩展,并且在尝试扩展的时候无法申请到足够的内存,或者在创建新的线程时没有足够的内存去创建对应的虚拟机栈,那么 Java 虚拟机将会抛出一个 OutOfMemoryError
异常。
在 Java 虚拟机中,堆(Heap)是可供各个线程共享的运行时内存区域,也是供所有类实例和数组对象分配内存的区域。
Java 堆是垃圾收集器管理的主要区域。从内存回收的角度看,由于收集器基本都采用了分代收集算法,所以 Java 堆可以分为:新生代(Young Generation)和老年代(Old Generation 或 Tenured Generation)。新生代又可以细分为 Eden 空间和 Survivor 空间。
注:Java 8 之后,没有了永久代(Permanent Generation),取而代之的是元空间(Metaspace)。元空间不在 Java 堆上,而在本地内存上。
在 Java 虚拟机中,方法区(Method Area)是可供各个线程共享的运行时内存区域,用于存储已被虚拟机加载的类的结构信息。例如,运行时常量池、字段和方法数据、构造函数和普通方法的字节码内容,还包括一些在类、实例、接口初始化时用到的特殊方法。
运行时常量池(Runtime Constant Pool)是 class 文件中每一个类或接口的常量池的运行时表示形式,它包括了若干种不同的常量,从编译期可知的数值字面量到必须在运行期才能获得的方法或字段引用。
Java 虚拟机实现可能会使用到传统的栈(通常称为 C Stack)来支持 native 方法的执行,这个栈就是本地方法栈(Native Method Stack)。当 Java 虚拟机使用其他语言(例如 C 语言)来实现指令集解释器时,也可以使用本地方法栈。