2.5 运行时数据区域
java虚拟机定义了多个用于程序执行期间的运行时数据区域。这些数据区域中一些随着java虚拟机的启动而创建,随着虚拟机的退出而销毁。其他的数据区域时和线程相关的。线程相关数据区域随着线程的创建而创建,随着线程的退出而销毁。
2.5.1 pc寄存器
java虚拟机支持多个线程同时执行。每个线程拥有自己的pc(program counter)寄存器。在任何时刻,一个线程只会执行一个方法,即线程的当前方法。如果执行的方法不是本地(native)方法,pc寄存器包含当前正在执行的虚拟机指令的地址。如果当前线程执行的是本地方法,虚拟机的pc寄存器中的值是未定义的。Java虚拟机的pc寄存器足够保存一个一个特定平台上的returnAddress或本地指针。
2.5.2 java虚拟机栈
每个线程都拥有一个私有的java虚拟机栈,和线程同时被创建。java虚拟机栈保存的栈是帧(frames)。java虚拟机栈和传统语言(比如c)的栈类似:它保存局部变量和部分结果,并在方法调用和返回中起作用。由于除了栈帧的入栈和出栈,Java虚拟机栈永远不会被直接操作,因此栈帧可以在堆中进行分配。Java虚拟机栈的内存不需要是连续的。
本规范允许Java虚拟机堆栈具有固定大小或根据计算的需要动态扩展和收缩。如果Java虚拟机栈具有固定大小,则可以在创建该栈时独立选择每个Java虚拟机栈的大小。
Java虚拟机的一个具体实现可以提供程序员或用户更改Java虚拟机栈初始大小的手段,以及在动态扩展或收缩Java虚拟机栈的情况下,控制最大和最小容量。
虚拟机栈可能出现以下异常:
- 如果一个线程需要的虚拟机栈大小超过允许的最大容量时,虚拟机栈将抛出stackOverflowError。
- 如果java虚拟机栈可以动态扩展,并且当进行扩展时没有申请到足够的内存完成扩展,或者没有足够的内存为新线程创建虚拟机栈,则Java 虚拟机抛出OutOfMemoryError。
2.5.3 Java 堆
Java虚拟机堆被所有的线程共享。java堆是运行时数据区域,用来分配类实例和数组的内存区域。
java堆在虚拟机启动时创建。对象的堆存储由自动存储管理系统(称为垃圾收集器)回收;对象永远不会被显式释放。java虚拟机并不假定任何特定的自动存储管理系统类型,存储管理技术会根据实现者的系统需求来选定。堆可以是固定大小,或者根据需要扩展和压缩不需要的容量。堆使用的内存不需要时连续的。
java堆和虚拟机栈类似,一个具体的实现可以提供开发者或者用户去控制堆的初始大小,也可以去控制一个动态扩展堆的最大和最小容量。
下面的异常条件和堆有关:
如果计算需要堆的容量超过了自动存储管理系统能给的最大容量,java虚拟机则抛出OutOfMermoryError。
2.5.4 方法区
java虚拟机的方法区(method area)被所有Java虚拟机线程共享。方法区类似于传统语言的编译代码的存储区或类似于操作系统进程中的“文本”段。它存储每个类的结构,例如运行时常量池,字段和方法数据,以及方法和构造函数的代码,包括类和实例初始化以及接口初始化中使用的特殊方法。
方法区在虚拟机启动时创建。尽管方法区在逻辑上是堆的一部分,但是简单的实现可以选择不进行垃圾回收和压缩。本规范未规定方法区域的位置或用于管理编译代码的策略。方法区可以是固定大小,或者根据需要扩展和压缩不需要的容量。堆使用的内存不需要时连续的。
java虚拟机实现可以提供开发者或者用户去控制方法区的初始大小,也可以去控制一个动态扩展方法区的最大和最小容量。
下面的异常条件和方法区有关:
如果方法区的没有可用的内存去满足一次内存分配请求,java虚拟机会抛出OutOfMermoryError。
2.5.5 运行时常量池
运行时常量池(run-time constant pool)是每一个类或者接口对应的class文件中constant_pool表的运行时表示形式。它包含几种常量,从编译时已知的数字的字面量到必须在运行时解析的方法和字段引用。运行时常量池提供类似于传统编程语言的符号表的功能,只是它包含比典型符号表范围更广的数据。
每个运行时常量池都是分配在java虚拟机的方法区。当Java虚拟机加载类或接口时,将构造类或接口的运行时常量池。
下面的异常条件和构建一个类或接口的运行时常量池有关:
当加载一个类或者接口时,如果构建运行时常量池需要的内存超过java虚拟机方法区能够提供的最大容量,虚拟机将抛出OutOfMermoryError。
2.5.6 本地方法栈
Java虚拟机的实现可以使用传统的栈,俗称“C stacks”,以支持本地方法(native methods)(用Java编程语言以外的语言编写的方法)。当Java虚拟机使用其他语言如C语言来实现java虚拟机指令集的解释器时,也可能用到本地方法栈。如果java虚拟机实现不能加载本地方法,它们自己也不需要依赖传统的栈,则没必要提供本地方法栈。如果支持的话,本地方法栈通常在线程创建的时候按照每个线程分配。
本规范允许本地方法栈具有固定大小或根据计算的需要动态扩展和收缩。如果本地方法栈具有固定大小,则可以在创建该栈时独立选择每个本地方法栈的大小。
java虚拟机实现可以提供开发者或者用户去控制本地方法栈的初始大小,也可以去控制一个支持动态扩展的本地方法栈的最大和最小容量。
下面的异常条件和本地方法栈有关:
- 如果一个线程需要的本地方法栈的容量超过允许的最大容量,那么虚拟机抛出
StackOverflowError。
- 如果本地方法栈支持动态扩展,当尝试扩展时没有足够的容量可用,或者没有足够的容量去为一个新线程创建一个初始的本地方法栈,java虚拟机抛出
OutOfMemoryError。