这个问题可能是愚蠢的,也可能是重复的 . 当程序引用该变量时,我对如何从堆栈中检索变量感到困惑 . 对象存储在堆中,位置存储在引用变量中,包含堆地址本身的引用变量存储在堆栈中 . 但JVM如何确定该引用变量存储在堆栈中的哪个位置 .
让我们考虑这个例子只是为了弄清楚我对此感到困惑 .
Class Test {
public void test() {
Object a = new Bar();
Object b = new Foo();
System.out.println(a);
}
}
让我们说方法test()正在执行 . 因此堆栈将被分配给test() .
现在当执行' Object a = new Bar(); '行时,将在Heap中创建Bar对象,并且实际变量 'a' (其值是Bar对象的地址位置)将存储在test()的堆栈中 .
再次在' Object b = new Foo(); '线上发生同样的事情 . Foo对象将在Heap中创建,实际变量 'b' (其值为Foo对象的地址位置)将存储在test()的堆栈中 .
现在,当执行' System.out.println(a); '行时,JVM如何知道堆栈中的哪个位置,应该检索 'a' 的值 . 意味着什么链接变量'a'及其在堆栈中的位置?
3 回答
你几乎就在那里,你的理解中只有一个缺失的环节 .
局部变量(或存储在局部变量中的对象的引用,如果我们讨论的是非基本类型)实际上存储在 local variable table 中,而不是存储在操作数堆栈中 . 当它们被呼叫使用时,它们只被推入堆栈 .
(令人困惑的是,局部变量表本身也存储在堆栈中,但是它是一个真实的表,具有固定的大小并且可以自由索引 . )
您可以使用
javap
查看代码生成的字节码 . 你会看到的是这样的:首先,这条线是什么?
元数据告诉JVM该方法的操作数堆栈不超过3个条目,3个局部变量并且取1个参数 . 但肯定是不对的,我们的方法不需要参数,显然只有2个局部变量!
答案是非静态方法总是有"0th argument":
this
. 这解释了参数计数,并引导我们进入下一个重要发现: arguments of a method are stored in the local variable table too . 因此我们的表将有条目0,1,2,其中0在开始时包含this
,而未初始化为1和2 .有了这个,让's look at the code! First up it'的行
0-7
:new
操作码创建Bar
的新实例并将引用存储在堆栈中 .dup
在堆栈顶部创建相同引用的副本(所以你现在有两个副本)invokespecial #3
调用Bar
的构造函数并使用堆栈的顶部 . (现在我们只剩下一份)astore_1
将剩余引用存储在本地变量号1
中(0
在本例中为this
)这就是
Object a = new Bar();
编译成的内容 . 然后你得到相同的Object b = new Foo();
(行8-15
) .然后是有趣的一点,来自
16
行:getstatic #6
在堆栈上推送System.out
的值aload_1
也会在堆栈上推送局部变量编号1(a
)invokevirtual #7
消耗这两个条目,在System.out
上调用println()
,其中a
作为其输入参数 .If you want to delve into it deeper, or you just want to point out my mistakes, the official reference for all of the above is here.
JVM存储堆栈帧,这些存储变量的数组 .
找到Here
JVM不是一个单一的数据结构,实际上它有几种不同的机制 . 执行程序时,JVM会组织所需的所有内存,并将它们分配到几个称为运行时数据区的不同内存堆栈中 .
这是一个更详细的解释:Architecture of JVM