所以我知道存在2个内存区域:堆栈和堆。
我也知道如果你创建一个局部变量,它将存在于堆栈中,而不是堆中。随着我们将数据推入其中,堆栈将会增长:
现在我将尝试将我遇到的困惑传递给你:
例如这个简单的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推入或者是这些单独的堆栈?
现在,如果我在第一个对象上调用一个方法,我将其推入堆栈,因为堆栈指针指向我推送的最后一个元素,它将如何处理?
答案 0 :(得分:13)
JVM中每个线程都有一个堆栈。每个堆栈由几个框架组成:每个方法调用都会创建一个新框架,当方法调用完成后,框架就会被销毁。
在堆栈框架内有两个区域:
根据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
转换为以下内容:
null
(一个指向“无”的特殊引用)推到操作数堆栈的顶部。operand stack __________ | null | <-- null is pushed on the operand stack |__________| | | |__________| | | |__________|
foo
。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件事:
- 本地变量表
- 操作数堆栈
- 对类的常量池AKA帧数据的引用
醇>
所以,push a null reference onto the stack
- &gt;将引用推送到操作数堆栈。
store a reference into local variable 1
- &gt;将引用存储到局部变量表
答案 2 :(得分:2)
您可以将操作数堆栈视为临时变量。它是每个方法调用的本地,它的大小可以在编译时确定。
如果要对任何类型的变量(局部变量,静态变量或非静态变量)执行任何操作,可以通过操作数堆栈执行此操作。 Java字节码指令主要使用操作数堆栈 。
例如,
foo = bar
将对应aload_2
和astore_1
,这只是意味着将局部变量2的值推送到操作数堆栈和 pop在操作数堆栈的顶部到局部变量1 if (foo == null) ...
将对应aload_1
和ifnonnull 5
,后者告诉JVM:如果操作数堆栈顶部的任何内容不为null,则跳转到下一个5指令抵消;否则,继续下一条指令。int x = args.length
将对应aload_0
,arraylength
,istore_3
,这意味着推送本地变量0 ,弹出数组在操作数堆栈顶部并将其长度推回,弹出整数并将其存储在局部变量3中 iadd
,isub
,imul
,idiv
等数值操作会从操作数堆栈中弹出两个整数值并将结果推回原来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 ---+