将变量推送到堆栈中的Stack和Variables差异?

时间:2015-04-29 14:07:11

标签: java memory-management jvm stack bytecode

所以我知道存在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推入或者是这些单独的堆栈?

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

4 个答案:

答案 0 :(得分:13)

JVM中每个线程都有一个堆栈。每个堆栈由几个框架组成:每个方法调用都会创建一个新框架,当方法调用完成后,框架就会被销毁。

在堆栈框架内有两个区域:

  1. 操作数堆栈(不要将此处的“堆栈”与JVM堆栈本身混淆 - 此处的堆栈表示该区域为先进先出结构)。
  2. 局部变量的数组,其中每个变量都有一个索引(从零开始)。
  3. 根据JVM的实现,它们在内存中可能连续也可能不连续。从逻辑上讲,它们是堆栈框架的两个独立部分。

    正如description of aconst_null中所述,aconst_null指令将null对象引用推送到操作数堆栈

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

      

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

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

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

        Object bar = null执行了相同的步骤,只是null存储在索引2的局部变量中。

        来源:Java虚拟机规范(参见this section)。

答案 1 :(得分:2)

你应该看看structure of Java stack frame

java 堆栈框架包含3件事:

  
      
  1. 本地变量表
  2.   
  3. 操作数堆栈
  4.   
  5. 对类的常量池AKA帧数据的引用
  6.   

所以,push a null reference onto the stack - &gt;将引用推送到操作数堆栈

store a reference into local variable 1 - &gt;将引用存储到局部变量表

的插槽1中

答案 2 :(得分:2)

您可以将操作数堆栈视为临时变量。它是每个方法调用的本地,它的大小可以在编译时确定。

如果要对任何类型的变量(局部变量,静态变量或非静态变量)执行任何操作,可以通过操作数堆栈执行此操作。 Java字节码指令主要使用操作数堆栈

例如,

  • foo = bar将对应aload_2astore_1,这只是意味着将局部变量2的值推送到操作数堆栈 pop在操作数堆栈的顶部到局部变量1
  • if (foo == null) ...将对应aload_1ifnonnull 5,后者告诉JVM:如果操作数堆栈顶部的任何内容不为null,则跳转到下一个5指令抵消;否则,继续下一条指令
  • int x = args.length将对应aload_0arraylengthistore_3,这意味着推送本地变量0 弹出数组在操作数堆栈顶部并将其长度推回弹出整数并将其存储在局部变量3中
  • iaddisubimulidiv等数值操作会从操作数堆栈中弹出两个整数值并将结果推回原来
  • 调用方法时,会弹出操作数堆栈并将其作为参数传递给新方法的局部变量。
  • putstatic / getstatic弹出/推送静态变量
  • putfield / getfield弹出/推送非静态变量

答案 3 :(得分:1)

它是相同的堆栈。

或者至少你可以认为它是同一个堆栈,它实际上取决于jvm的实现。

在简单的jvm中

调用方法时,它会为堆栈上的局部变量保留空间。它基本上将堆栈指针递增到它的局部变量的开放空间。方法的父对象(如果是实例方法)和方法的参数是第一个本地。

要将堆栈中的内容分配给本地var,请从堆栈顶部复制到附近的某个位置,即之前的几个位置,位于同一内存区域。

在您的示例中astore 1期间:

locals/stack
[local 0] // args
[local 1] // foo   <--+
[local 2] // bar      |
..return address..    |
[stack 0] // null  ---+