运行时数据区 (Run-Time Data Areas)
Java虚拟机在程序执行期间定义了各种
运行时数据区
,一部分数据区的生命周期与Java虚拟机一致,另一部分数据区的生命周期与线程一致。
1. 程序计数器 (program counter register)
- 程序计数器是
线程私有的
,生命周期随线程而生而灭。 - 在任何时候,每个线程都在执行一个方法。如果该方法是
native
方法,程序计数器会存储当前jvm指令的内存地址。反之,程序计数器的值将不会被定义。 - 程序计数器(空间)足够大,可以保存一个
returnAddress
类型的值或者特定平台上的本地指针。
2. Java虚拟机栈 (java virtual machine stacks)
- Java虚拟机栈是
线程私有
的,生命周期随线程而生而灭。 - Java虚拟机栈的使用的内存可以是不连续的。
- Java虚拟机栈存储
栈帧
。每个方法从调用到执行完成的过程,对应着一个栈帧
在Java虚拟机栈中的压栈
到弹栈
的过程。 - Java虚拟机栈可以具有固定大小,也可以根据计算要求动态扩展或收缩。
- 如果Java虚拟机允许动态扩展,但是扩展时却无法申请到足够的内存,则会抛出
OutOfMemoryError
异常。 - 如果线程请求分配的栈容量大于Java虚拟机栈允许的最大容量,则会抛出
StackOverflowError
异常。
3. 堆 (heap)
- 堆存储对象实例。
- 堆内存可以是不连续的。
- 堆是
线程共享
的,生命周期与Java虚拟机一致。 - 堆可以是固定大小,也可以根据需求动态扩展和收缩。
- 只要Java虚拟机中的对象不断被创建,且保证
GC Roots
到对象之间有可达路径
来避免垃圾回收机制,当堆存储的对象到达堆内存允许的最大容量时,会抛出OutOfMemoryError
异常。
4. 方法区 (method area)
- 方法区是
线程共享的
,生命周期与Java虚拟机一致 - 方法区存储类信息,运行时常量池(
runtime constant pool
),字,方法数据,JIT编译后的代码数据
,以及类和接口初始化的函数<clinit>
和类实例初始化的函数<init>
等信息。 - 方法区在逻辑上是
堆
的一部分,但是两者应该区分来看。 - 方法区的内存区域可以是不连续的。
- 方法区的大小可以是固定的,也可以根据需求动态的扩缩容。
- 如果无法提供方法区中的内存来满足分配的请求,会抛出
OutOfMemoryError
异常。
5. 运行时常量池 (runtime constant pool)
- 每个运行时常量池都是从Java虚拟机的
方法区
中分配的 - 运行时常量池主要用于存放编译期生成的各种字面量和符号引用,以及在运行时解析的方法和字段引用。
- 创建类或接口时,如果运行时常量池构造所需的内存超过Java虚拟机的方法区域中可用的内存,则Java虚拟机将抛出
OutOfMemoryError
6. 本地方法栈 (native method stacks)
- 本地方法栈和
Java虚拟机栈
很相似。 - 本地方法栈也是
线程私有
的,并且也能抛出OutOfMemoryError
,StackOverflowError
异常。 - 本地方法栈为虚拟机使所使用到的
native
方法服务。 - Java虚拟机中并没有限制实现
native
方法的语言,使用方式和数据结构,因此具体的虚拟机可以自由的实现它。 - 有的虚拟机(Sun HotSpot虚拟机)直接把本地方法栈和虚拟机栈合并在一起