Java虚拟机 (JVM) 堆栈区
对于每个线程,JVM 在线程创建时创建一个单独的堆栈。 Java虚拟机堆栈的内存不需要是连续的。 Java虚拟机只直接在Java堆栈上执行两个操作:压入和弹出帧。特定线程的堆栈可以称为运行时堆栈。该线程执行的每个方法调用都存储在相应的运行时堆栈中,包括参数、局部变量、中间计算和其他数据。完成一个方法后,从堆栈中删除相应的条目。完成所有方法调用后,堆栈变为空,并且该空堆栈在终止线程之前被 JVM 销毁。堆栈中存储的数据可供相应线程使用,而其余线程无法使用。因此我们可以说本地数据线程安全。堆栈中的每个条目称为堆栈帧或激活记录。
堆栈框架结构
堆栈帧基本上由三部分组成:局部变量数组、操作数堆栈和帧数据。当 JVM 调用Java方法时,它首先检查类数据以确定该方法在局部变量数组中所需的字数(局部变量数组和操作数堆栈的大小,以每个单独方法的字数来衡量)和操作数栈。它为调用的方法创建一个适当大小的堆栈帧,并将其推送到Java堆栈上。
1. 局部变量数组 (LVA):
- 堆栈帧的局部变量部分被组织为从零开始的单词数组。
- 它包含方法的所有参数和局部变量。
- 数组中的每个槽或条目为 4 个字节。
- int、float 和reference 类型的值占用数组中的1 个条目或槽,即4 个字节。
- double 和 long 的值占用数组中的 2 个连续条目,即总共 8 个字节。
- Byte、short和 char 值在存储前会转换为 int 类型,占用 1 个插槽,即 4 个字节。
- 但是存储布尔值的方式因JVM而异。但是大多数 JVM 为局部变量数组中的布尔值提供了 1 个插槽。
- 参数首先按照声明的顺序放入局部变量数组中。
- 例如:让我们考虑一个类 Example 有一个方法bike()那么局部变量数组将如下图所示:
// Class Declaration
class Example
{
public void bike(int i, long l, float f,
double d, Object o, byte b)
{
}
}
2. 操作数栈(OS):
- JVM 使用操作数堆栈作为工作空间,就像粗略的工作一样,或者我们可以说用于存储中间计算的结果。
- 操作数栈被组织成一个字数组,就像一个局部变量数组。但这不是通过使用像局部变量数组这样的索引来访问的,而是通过一些可以将值推送到操作数堆栈的指令和一些可以从操作数堆栈中弹出值的指令以及一些可以执行所需操作的指令来访问。
- 例如:以下是 JVM 如何使用以下代码,该代码将减去两个包含两个 int 的局部变量,并将 int 结果存储在第三个局部变量中:
- 所以这里的前两条指令iload_0和iload_1将从局部变量数组中压入操作数堆栈中的值。并且指令isub将减去这两个值并将结果存储回操作数堆栈,在istore_2之后,结果将从操作数堆栈中弹出并存储到位置 2 的局部变量数组中。
3.帧数据(FD):
- 它包含所有符号引用(常量池解析)和与该特定方法相关的正常方法返回。
- 它还包含对异常表的引用,该表在异常情况下提供相应的捕获块信息。