将变量推送到堆栈和存在于堆栈差异中的变量?

2022-09-04 20:05:52

所以我知道存在2个内存区域:堆栈

我还知道,如果您创建一个局部变量,它将存在于堆栈中,而不是堆中。堆栈将随着我们将数据推送到其中而增长,例如:

enter image description here

现在,我将尝试将我所遇到的困惑传递给您:

例如,这个简单的Java代码:

public class TestClass {
    public static void main(String[] args)  {
        Object foo = null;
        Object bar = null;
    }
}

被翻译成以下字节码:

public static void main(java.lang.String[]);
  Code:
   Stack=1, Locals=3, Args_size=1
   0:   aconst_null
   1:   astore_1
   2:   aconst_null
   3:   astore_2
   4:   return

LineNumberTable: 
line 5: 0
line 6: 2
line 7: 4

LocalVariableTable: 
Start  Length  Slot  Name   Signature
0      5      0    args       [Ljava/lang/String;
2      3      1    foo       Ljava/lang/Object;
4      1      2    bar       Ljava/lang/Object;

其中,根据定义,acons_null是:

push a null reference onto the stack

astore_1是:

store a reference into local variable 1

我感到困惑的是,我们将foo推入堆栈,然后我们再次将其存储在堆栈中?在局部变量中存储引用是什么意思?局部变量位于何处?我们把foo推入的同一堆栈还是这些单独的堆栈?

现在,如果我在推送到堆栈中的第一个对象上调用一个方法,由于堆栈指针指向我推送的最后一个元素,它将如何处理?


答案 1

JVM 中每个线程存在一个堆栈。每个堆栈由几个组成:每个方法调用创建一个新帧,当方法调用完成时,帧被销毁。

在堆栈框架中,有两个区域:

  1. 操作数堆栈(不要将此处的“堆栈”一词与 JVM 堆栈本身混淆 -- 此处的堆栈将区域表示为后进先出结构)。
  2. 局部变量的数组其中每个变量都有一个索引(从零开始)。

根据 JVM 实现的不同,它们在内存中可能是连续的,也可能不是连续的。从逻辑上讲,它们是堆栈框架的两个独立部分。

aconst_null 的描述中所述,该指令将对象引用推送到操作数堆栈上。aconst_nullnull

正如astore_<n>的描述中所解释的那样(其中可以是0,1,2或3):n

必须是当前帧的局部变量数组的索引 (§2.6)。操作数堆栈顶部的 必须为 类型 或 类型。它从操作数堆栈中弹出,局部变量的值设置为 objectref <n><n>objectrefreturnAddressreference

因此,在您的示例中,该语句将转换为以下内容:Object foo = null

  1. 将 (指向“无”的特殊引用)推送到操作数堆栈的顶部。null
  operand stack
   __________
  |   null   | <-- null is pushed on the operand stack
  |__________|
  |          |
  |__________|
  |          |
  |__________|
  1. 从操作数堆栈中弹出引用,并将其存储在索引 1 处的局部变量中。此局部变量对应于 。foo
  operand stack                           local variables
   __________      _______________ _______________ _______________ _______________
  |          |    |      args     |   foo (null)  |               |               |
  |__________|    |_______0_______|_______1_______|_______2_______|_______3_______|
  |          |                    store null in LV#1 
  |__________|
  |          |
  |__________|

除了存储在索引 2 处的局部变量中之外,还执行了相同的步骤。Object bar = nullnull

资料来源:Java 虚拟机规范(请参阅本节)。


答案 2

你应该看看Java堆栈框架的结构

一个java堆栈框架包含3个东西:

  1. 局部变量表
  2. 操作数堆栈
  3. 对类的常量池 AKA 帧数据的引用

因此,-->将引用推送到操作数堆栈上。push a null reference onto the stack

store a reference into local variable 1-->将引用存储到局部变量表的插槽 1 中