运行时数据区
Java虚拟机运行时将其管理的内存区域划分为几个数据区。包括以下几个:
-
虚拟机栈(VM Stack)
- 虚拟机栈也是线程私有的。生命周期与线程相同。
- 虚拟机栈描述的是
Java方法执行的线程内存模型
:每个方法执行时,Java虚拟机会同步创建一个栈帧(Stack Frame)
用于存储局部变量表
,操作数栈
,动态链接
,方法出口
等信息。一个方法从执行到结束,对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。 - 对于C/C++中所谓的堆内存和栈内存,在Java中划分的要更为细腻一些。栈通常指的就是这里的虚拟机栈,或者更多情况下指的就是虚拟机栈的
局部变量表
部分。 - 局部变量表存放了编译器克制的各种Java虚拟机基本数据结构(boolean, byte, char, short, int, long, float, double)、对象引用(reference类型,指向对象起始地址的指针,或者指向一个代表对象的句柄或者其他与此对象相关的位置)和returnAddress类型(指向一条字节码指令的地址)。
- 上述数据结构在局部变量表中的存储空间以
局部变量槽
(slot)来表示的。其中64位的long和double型占两个槽,其余占1个。具体的每个槽占32bit还是64bit,取决于具体的虚拟机如何实现。 - 《Java虚拟机规范》中,对此内存区域规定了两类异常情况:
- 线程请求栈深超过虚拟机所允许栈深,抛
StackOverflowError
异常。 - 若虚拟机栈容量可动态伸展,栈扩展是无法申请足够内存时,抛
OutOfMemoryError
异常。
- 线程请求栈深超过虚拟机所允许栈深,抛
-
本地方法栈(Native Method Stack)
该内存区域发挥的作用类似虚拟机栈,只是为本地方法提供服务。有的虚拟机(如HotSpot)直接把本地方法栈和虚拟机栈合为一体。抛异常也同虚拟机栈。
-
程序计数器(Program Counter Register)
-
是一块较小的内存空间 。是程序控制流的指示器。可以看做当前线程所执行java字节码的行号指示器。
-
是程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都要依赖这个计数器来完成。
-
线程私有。
-
若线程执行的是一个Java方法,PCR记录的是正在执行的虚拟机字节码指令的地址;如果是本地方法(Native),这个计数器的值应为空。
-
-
堆区(Heap)
- Java堆(Java Heap)是虚拟机所管理的内存中最大的一块儿。堆是所有线程共享的内存区域,在虚拟机启动时创建。此内存区域的唯一作用就是存放
对象实例
,java世界里几乎所有的对象实例都在这里分配内存。 - Java堆是垃圾收集器管理的内存区域。GC操作的目标区域。
- 堆可以处于物理上不连续的内存空间中,但在逻辑上它应该被视为连续的。堆即可被实现为固定的,也可被实现为可扩展的,不过当前主流虚拟机都将其实现为可扩展的。(通过参数 -Xms 和 -Xmx设定)。
- 如果在Java堆中没有内存完成实例分配,并且堆也无法再扩展时,抛
OutOfMemoryError
异常。
- Java堆(Java Heap)是虚拟机所管理的内存中最大的一块儿。堆是所有线程共享的内存区域,在虚拟机启动时创建。此内存区域的唯一作用就是存放
-
方法区(Method Area)
- 方法区同Java堆一样,是各个线程共享的内存区域,用于存储已被虚拟机加载的
类型信息
、常量
、静态变量
、即时编译器编译后的代码缓存
等数据。《Java虚拟机规范》中把方法区描·述为堆的一个逻辑部分,但它却有一个别名“非堆”(Non-Heap),目的是与Java堆区分开来。 - **static final Class 常量池 **放在方法区
- 方法区同Java堆一样,是各个线程共享的内存区域,用于存储已被虚拟机加载的
方法区指的是理论概念。
持久代/元空间指的是实现机制。
JDK 1.6 及之前:有永久代,字符串常量池和运行时常量池都存在持久代。
JDK 1.7 : 有永久代,字符串常量池移到堆区 运行时常量池还在方法区。
JDK 1.8 及之后 :无永久代,字符串常量池在堆区,运行时常量池在元空间。