为了账号安全,请及时绑定邮箱和手机立即绑定

Java中栈内存是如何访问的

Java中栈内存是如何访问的

DIEA 2023-05-10 17:34:08
我试图了解 Java 堆栈和堆内存。通过阅读不同的资料,我得出以下结论:本地(方法的一部分)声明的基本类型如 int、bytes、char 等存储在堆栈中。而静态和实例(对象实例或类的一部分)原始类型存储在堆上。简而言之,局部变量存储在栈上,而实例和静态变量则存储在堆上。本地引用对象将在堆中创建,而变量将在堆栈中创建。实例/静态引用对象将在堆中创建变量和对象。(例如,一个本地的,将在堆栈中car ferrari = new car()创建一个内存,而实际对象将在堆中。ferrari堆栈变量ferrari将具有实际对象的堆位置的引用地址)。但是在进一步阅读时,我读到在堆栈中内存被分配到前一帧的顶部。这让我对以下情况感到困惑:问题一:让我的方法是:calculate(){int a=15;int b=25;return a+b;}但根据堆栈,后入将是 b。所以 a 的值将超出范围,直到从堆栈中使用 b 的值。程序如何能够在 'b' 之前使用 'a' 的值?对于操作a+b。问题2:让我的方法是:calculate(){int a=15;int b=25;int z= a+b;int y=a+b;return y;}现在我已经使用变量 a 和 b 来计算'z',变量 y 是如何能够再次访问 a 和 b 的,因为它已经从堆栈中使用了。我不是Java专家,如果有人能用简单的语言解释一下,我将非常感谢。
查看完整描述

1 回答

?
慕桂英3389331

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();

}

堆栈看起来像这样:

//img1.sycdn.imooc.com//645b653900012f6d01660152.jpg

只有这张图片h在范围内。但是在bar()方法完成后,变量y和 都z将在范围内。

在内部,stack 包含局部变量 array和 操作数 stack

在局部变量数组中存储了方法的参数和该堆栈帧的所有局部变量。此外,如果堆栈帧表示实例方法,则局部变量数组的位置0将是this指向堆上对象的引用。如果方法static在位置上,则该位置0将是方法的第一个参数。局部变量数组的大小在编译时确定。要查看局部变量数组,您需要查看已编译的 Java 代码。对于您的第一种calculate()方法(以防万一static),局部变量数组将如下所示:

//img1.sycdn.imooc.com//645b6543000109cd03240068.jpg

您可以看到变量名称(Name列)与其在数组中的位置(Slot列)之间的关系。

操作数栈是用于对局部变量数组中的变量进行操作的栈。在操作数堆栈上是来自局部变量数组的变量,在您的示例中,添加并分配给其他变量,这些变量再次保存在局部变量数组中。

问题 1: 程序如何使用abefore的值b进行操作a + b

因为有pop操作读取然后从堆栈中删除值。要添加这两个值,您需要从操作数堆栈 (value b) 中弹出(读取并删除)最后一个值。a现在是操作数栈顶部的值。弹出(读取并删除)这个值(操作数堆栈现在为空)并添加这两个值。将结果放回操作数栈。操作数栈现在只包含结果。

详细说明:
当您调用calculate()新的堆栈框架时,它会被创建并放在堆栈的顶部。语句int a = 15;在内部分为 2 个步骤:

  1. 常量15放在操作数栈的顶部,

  2. 弹出(读取并删除)操作数堆栈中的值(这是常量15)并将其保存在特定索引处的局部变量数组中。

对 重复相同的步骤int b = 25;。现在您有 2 个值保存在局部变量数组和空操作数堆栈中。最后一部分return a + b;通过 4 个步骤完成:

  1. a将局部变量数组中的值推送(加载)到操作数堆栈,

  2. b将值从局部变量数组推入(加载)到操作数堆栈。现在您在操作数堆栈上有两个值。

  3. 从操作数堆栈中弹出(读取并删除)第一个值(这是b值)。从操作数堆栈弹出第二个值(这是a值)。此时操作数栈是空的。将这两个值相加并将结果推回操作数堆栈。现在操作数栈只包含加法运算的结果。

  4. 从操作数栈中弹出值并将其返回给调用此方法的任何人。现在操作数栈是空的。

此时,由于方法已完成,栈帧从栈中移除。

问题 2: 现在我已经使用了变量ab计算z,变量如何y能够访问ab再次使用,因为它已经从堆栈中使用了?

因为栈帧中有一个局部变量数组,它存储了该栈帧中定义的所有局部变量。

详细说明:
int a = 15int b = 25Q1一样。int z = a + b;是这样执行的:

  1. a将局部变量数组中的值推送(加载)到操作数堆栈,

  2. b将值从局部变量数组推入(加载)到操作数堆栈。现在您在操作数堆栈上有两个值。

  3. 从操作数堆栈中弹出(读取并删除)第一个值(这是b值)。从操作数堆栈弹出第二个值(这是a值)。此时操作数栈是空的。将这两个值相加并将结果放入操作数堆栈。现在操作数栈只包含加法运算的结果。

  4. 弹出(读取并删除)操作数堆栈(结果)中的值a + b并将其保存在特定索引处的局部变量数组中。

int y = a + b;以与 相同的方式执行int z = a + b;

最后一部分return y

  1. y值从局部变量数组压入操作数栈,

  2. 从操作数栈中弹出值并将其返回给调用此方法的任何人。操作数栈现在是空的。

因为方法已经完成,栈帧从栈中移除。

请注意,有堆栈,它包含在多个堆栈帧中,而且在每个堆栈帧内,您都有用于该堆栈帧内操作的操作数堆栈。


查看完整回答
反对 回复 2023-05-10
  • 1 回答
  • 0 关注
  • 148 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信