1 回答
TA贡献2036条经验 获得超8个赞
每个线程都有自己的堆栈。
每当一个线程调用一个方法时,都会在堆栈内存中为该方法创建一个新块来保存本地原始值和对该方法中其他对象的引用。这个内存块称为栈帧。一旦方法结束,该块将变为未使用状态,并将其从堆栈顶部移除。这是因为堆栈内存始终以 LIFO(后进先出)顺序引用。
所以,如果你有代码:
void go(){
int x = 1;
foo(x);
}
void foo(int y){
int z = y;
bar();
}
void bar(){
Hello h = new Hello();
}
堆栈看起来像这样:
只有这张图片h
在范围内。但是在bar()
方法完成后,变量y
和 都z
将在范围内。
在内部,stack 包含局部变量 array和 操作数 stack。
在局部变量数组中存储了方法的参数和该堆栈帧的所有局部变量。此外,如果堆栈帧表示实例方法,则局部变量数组的位置0
将是this
指向堆上对象的引用。如果方法static
在位置上,则该位置0
将是方法的第一个参数。局部变量数组的大小在编译时确定。要查看局部变量数组,您需要查看已编译的 Java 代码。对于您的第一种calculate()
方法(以防万一static
),局部变量数组将如下所示:
您可以看到变量名称(Name
列)与其在数组中的位置(Slot
列)之间的关系。
操作数栈是用于对局部变量数组中的变量进行操作的栈。在操作数堆栈上是来自局部变量数组的变量,在您的示例中,添加并分配给其他变量,这些变量再次保存在局部变量数组中。
问题 1: 程序如何使用a
before的值b
进行操作a + b
?
因为有pop操作读取然后从堆栈中删除值。要添加这两个值,您需要从操作数堆栈 (value b
) 中弹出(读取并删除)最后一个值。a
现在是操作数栈顶部的值。弹出(读取并删除)这个值(操作数堆栈现在为空)并添加这两个值。将结果放回操作数栈。操作数栈现在只包含结果。
详细说明:
当您调用calculate()
新的堆栈框架时,它会被创建并放在堆栈的顶部。语句int a = 15;
在内部分为 2 个步骤:
常量
15
放在操作数栈的顶部,弹出(读取并删除)操作数堆栈中的值(这是常量
15
)并将其保存在特定索引处的局部变量数组中。
对 重复相同的步骤int b = 25;
。现在您有 2 个值保存在局部变量数组和空操作数堆栈中。最后一部分return a + b;
通过 4 个步骤完成:
a
将局部变量数组中的值推送(加载)到操作数堆栈,b
将值从局部变量数组推入(加载)到操作数堆栈。现在您在操作数堆栈上有两个值。从操作数堆栈中弹出(读取并删除)第一个值(这是
b
值)。从操作数堆栈弹出第二个值(这是a
值)。此时操作数栈是空的。将这两个值相加并将结果推回操作数堆栈。现在操作数栈只包含加法运算的结果。从操作数栈中弹出值并将其返回给调用此方法的任何人。现在操作数栈是空的。
此时,由于方法已完成,栈帧从栈中移除。
问题 2: 现在我已经使用了变量a
和b
计算z
,变量如何y
能够访问a
并b
再次使用,因为它已经从堆栈中使用了?
因为栈帧中有一个局部变量数组,它存储了该栈帧中定义的所有局部变量。
详细说明:int a = 15
和int b = 25
Q1一样。int z = a + b;
是这样执行的:
a
将局部变量数组中的值推送(加载)到操作数堆栈,b
将值从局部变量数组推入(加载)到操作数堆栈。现在您在操作数堆栈上有两个值。从操作数堆栈中弹出(读取并删除)第一个值(这是
b
值)。从操作数堆栈弹出第二个值(这是a
值)。此时操作数栈是空的。将这两个值相加并将结果放入操作数堆栈。现在操作数栈只包含加法运算的结果。弹出(读取并删除)操作数堆栈(结果)中的值
a + b
并将其保存在特定索引处的局部变量数组中。
int y = a + b;
以与 相同的方式执行int z = a + b;
。
最后一部分return y
:
将
y
值从局部变量数组压入操作数栈,从操作数栈中弹出值并将其返回给调用此方法的任何人。操作数栈现在是空的。
因为方法已经完成,栈帧从栈中移除。
请注意,有堆栈,它包含在多个堆栈帧中,而且在每个堆栈帧内,您都有用于该堆栈帧内操作的操作数堆栈。
添加回答
举报