java当堆栈弹出时内存会发生什么变化?
我有一个函数
public void f() {
int x = 72;
return;
}
因此x
可能存储在地址0x9FFF
处
当函数返回时,该地址的内存发生了什么变化?它还在吗?也就是说,值是否仍然72
?还是完全无效
你可以在下面搜索框中键入要查询的问题!
我有一个函数
public void f() {
int x = 72;
return;
}
因此x
可能存储在地址0x9FFF
处
当函数返回时,该地址的内存发生了什么变化?它还在吗?也就是说,值是否仍然72
?还是完全无效
# 1 楼答案
我不是随便看规范,但我猜这在技术上是没有定义的
<>我实际上在C++中尝试过类似的东西,实际上是^ {CD1>}(或者我在函数调用之前放的任何东西)如果我没有记错的话,所以机器实际上没有经过并写入^ {CD2>}到那个位置或某物。其中一些也是实现细节。我也用MIPS汇编语言实现了它(如果我能找到它,我将包括一个代码示例)。基本上,当我需要寄存器时,我只需按我需要的本地变量的多少来“增长”堆栈,存储我需要的寄存器中的当前值(以便以后恢复它们),然后重新使用寄存器。如果这是实现,那么该值实际上可以包含调用者中的局部变量的值。不过,我不认为Java正是这么做的
TL;DR这是一个实现细节,但在C语言中,至少在实际需要之前,它不会覆盖内存中的值。Java更难预测
# 2 楼答案
Java中的基元类型放在堆栈上(放在frame的局部变量数组中)。每次调用方法时都会创建一个新框架:
当框架的方法调用完成时,框架将被销毁。此时,所有局部变量和部分结果可能仍驻留在堆栈上,但它们已被放弃,不再可用
# 3 楼答案
Java编程语言和Java虚拟机都没有定义在弹出堆栈帧之后堆栈帧的内存会发生什么。这是一个低级实现细节,被高级抽象所掩盖。事实上,Java语言和JVM字节码在设计上使得从堆栈中检索已经删除的值变得不可能(与C/C++不同)
然而,在实践中,Java中的堆栈帧的行为与C中的堆栈帧类似。增加堆栈会使其指针(通常向下)跳动,并分配空间来存储变量。收缩堆栈通常会使指针向上移动,只会让内存中的旧值腐烂,而不会覆盖它们。如果您对JVM的堆栈内存区域具有低级访问权限,那么这就是您应该看到的行为
请注意,在Java中不可能像C那样尝试读取未初始化的堆栈变量:
JVM实现中可能存在其他堆栈行为。例如,当弹出帧时,可以将4 KiB虚拟内存页取消映射回操作系统,这将实际擦除旧值。同样在机器架构(如Mill)上,堆栈内存也得到了特殊处理,因此增加堆栈总是会返回一个填充了零字节的区域,这节省了实际从内存加载旧值的工作
# 4 楼答案
在C中,它是未定义的行为
在实践中,如果您尝试以下方法:
然后,在大多数C实现中,
foo()
很可能会打印72
。这是因为尽管ptr
引用的地址可以重新分配,但它不太可能被重新分配,并且没有任何内容覆盖该内存。程序继续运行的时间越长,初始化更多的局部变量,并调用malloc()
,该内存地址被重用的可能性就越大,值也会发生变化然而,C规范中并没有规定必须这样做——一个实现一旦超出范围,就可能将该地址归零,或者在您试图读取它时使运行时恐慌,或者,嗯,任何事情——这就是“未定义”的意思
作为一名程序员,你应该注意避免这样做。很多时候,它会引起的错误会很明显,但有些时候你会引起间歇性的错误,它们是最难追踪到的
在Java中,虽然内存在超出范围后仍可能包含
72
,但实际上无法访问它,因此它不会影响程序员。在Java中访问它的唯一方法是对它进行“官方”引用,在这种情况下,它不会被标记为垃圾收集,也不会真正超出范围