什么是操作数堆栈?

时间:2014-06-26 09:35:07

标签: java jvm

我正在阅读有关JVM架构的内容。今天我读到了Operand Stack的概念。根据一篇文章:

  

在执行字节代码指令期间使用操作数堆栈   与通用寄存器在本机CPU中使用的方式类似。

我无法理解:操作数堆栈究竟是什么,它在jvm中是如何工作的?

3 个答案:

答案 0 :(得分:56)

各种字节码操作如何获取输入,以及它们如何提供输出。

例如,考虑iadd操作,它将两个int加在一起。要使用它,您可以在堆栈上推送两个值,然后使用它:

iload_0     # Push the value from local variable 0 onto the stack
iload_1     # Push the value from local variable 1 onto the stack
iadd        # Pops those off the stack, adds them, and pushes the result

现在堆栈的最高值是这两个局部变量的总和。下一个操作可能会占用顶部堆栈值并将其存储在某个位置,或者我们可能会在堆栈上推送另一个值来执行其他操作。

假设您要一起添加三个值。堆栈使这很容易:

iload_0     # Push the value from local variable 0 onto the stack
iload_1     # Push the value from local variable 1 onto the stack
iadd        # Pops those off the stack, adds them, and pushes the result
iload_2     # Push the value from local variable 2 onto the stack
iadd        # Pops those off the stack, adds them, and pushes the result

现在堆栈的最高值是将这三个局部变量加在一起的结果。

让我们更详细地看一下第二个例子:

我们假设:

  • 堆栈是空的,以开头(这几乎从来都不是真的,但我们在开始之前并不关心它上面的内容)
  • 局部变量0包含27
  • 本地变量1包含10
  • 本地变量2包含5

所以最初:

+-------+
| stack |
+-------+
+-------+

然后我们

iload_0     # Push the value from local variable 0 onto the stack

现在我们有了

+-------+
| stack |
+-------+
|   27  |
+-------+

下一步

iload_1     # Push the value from local variable 1 onto the stack
+-------+
| stack |
+-------+
|   10  |
|   27  |
+-------+

现在我们进行补充:

iadd        # Pops those off the stack, adds them, and pushes the result

它" pops"离开堆栈的1027,将它们加在一起,然后推送结果(37)。现在我们有:

+-------+
| stack |
+-------+
|   37  |
+-------+

第三次int的时间:

iload_2     # Push the value from local variable 2 onto the stack
+-------+
| stack |
+-------+
|    5  |
|   37  |
+-------+

我们做了第二个iadd

iadd        # Pops those off the stack, adds them, and pushes the result

这给了我们:

+-------+
| stack |
+-------+
|   42  |
+-------+

(当然,这是Answer to the Ultimate Question of Life the Universe and Everything。)

答案 1 :(得分:13)

操作数堆栈保存运算符用于执行操作的操作数。操作数堆栈上的每个条目都可以包含任何Java虚拟机类型的值。

来自JVM specifications

  

Java虚拟机指令从操作数中获取操作数   堆栈,对它们进行操作,并将结果推回操作数   堆。操作数堆栈也用于准备参数   传递给方法并接收方法结果。

例如,iadd指令将添加两个整数值,因此它将从操作数堆栈中弹出前两个整数值,并在添加后将结果推入操作数堆栈。

有关详细信息,请查看JVMS#2.5 : Run-Time Data Areas

在操作数堆栈的上下文中总结它,

    _______________________________
   |        _____________________  |
   |       |         + --------+ | |
   |  JVM  |         | Operand | | | 
   | Stack |  FRAME  |  Stack  | | |
   |       |         +---------+ | |
   |       |_____________________| |
   |_______________________________|
  • JVM支持多线程执行环境。每个线程 execute在创建线程的同时创建了私有java虚拟机堆栈(JVM Stack)。
  • 此Java虚拟机堆栈存储帧。 Frame保存数据,部分结果,方法返回值并执行动态链接。
  • 每个帧都包含堆栈,称为操作数堆栈,它保存JVM类型的操作数值。操作数堆栈的深度在编译时确定并使用运算符进行更新。

答案 2 :(得分:6)

  

但难道不能确切地知道它是什么以及它在jvm中是如何工作的?

JVM定义虚拟计算机,该计算机的指令集基于堆栈。这意味着JVM指令集中的指令通常会从堆栈中推送和弹出操作数。例如,

  • 加载指令可能从局部变量,实例变量或类变量中获取值并将其推送到操作数堆栈上,
  • 算术指令将从操作数堆栈中弹出值,执行计算并将结果推回堆栈,
  • 商店指令会从堆栈中弹出一个值并存储它......

@ T.J.Crowder的回答提供了一个更具体的例子。

如何实现操作数堆栈是特定于平台的,它取决于是否正在解释代码或是否已对其进行JIT编译。

  • 在解释的情况下,操作数堆栈可能是由解释器代码管理的数组。推送和弹出微操作将实现如下:

        stack[top++] = value;
    

        value = stack[--top];
    
  • 当代码被JIT编译时,字节码指令序列被转换为本机指令序列,实现与字节码相同的功能。操作数堆栈位置被映射到本机寄存器或存储器位置;例如在当前的本机堆栈框架中。映射涉及各种优化,旨在使用寄存器(快速)优先于内存(较慢)。

    因此在JIT编译的情况下,操作数堆栈不再具有明确的物理存在,但编译程序的整体行为操作数堆栈确实存在相同 1


1 - 实际上,考虑到Java内存模型时,它可能不完全相同。然而,存储器模型对差异可能存在明确的界限。对于不与外部交互的单线程计算(例如I / O,时钟等),可能没有可观察到的差异。