[英]What happens to memory when stack is popped?
我有一个功能
public void f() {
int x = 72;
return;
}
因此x
可能存储在地址0x9FFF
。
函数返回后,该地址处的存储器将如何处理? 还在吗? 即该值仍然是72
? 还是完全无效?
弹出框架后,Java编程语言和Java虚拟机均未定义堆栈框架的内存发生了什么。 这是一个较低层的实现细节,被较高层的抽象所掩盖。 实际上,Java语言和JVM字节码使设计无法从堆栈中检索已删除的值(与C / C ++不同)。
但是实际上,Java中的堆栈框架的行为类似于C中的堆栈框架。不断增长的堆栈将碰撞其指针(通常朝下)并分配空间来存储变量。 缩小堆栈通常会向上移动指针,并简单地将旧值保留在内存中以使其腐烂而不覆盖它们。 如果您具有对JVM的堆栈内存区域的低级访问权限,这是您应该期望看到的行为。
请注意,在Java中不可能像C这样的技巧尝试读取未初始化的堆栈变量:
static boolean firstTime = true;
public void f() {
int x;
if (firstTime) {
x = 72;
firstTime = false;
} else {
// Compile error: Variable 'x' may not have been initialized
System.out.println(x);
}
}
在JVM实现中,其他堆栈行为也是可能的。 例如,当弹出框架时,可以将4 KiB虚拟内存页面取消映射回操作系统,这实际上将擦除旧值。 同样在机器架构(例如Mill)上 ,对堆栈存储器也进行了特殊处理,从而使堆栈不断增长将始终返回一个填充有零字节的区域,从而节省了从内存实际加载旧值的工作。
在C语言中,这是未定义的行为 。
在实践中,如果您尝试以下操作:
int *ptr;
void foo() {
bar();
printf("%d", *ptr);
}
void bar() {
int x = 72;
ptr = &x;
}
然后,很可能是在C大多数实现, foo()
将打印72
。 这是因为尽管ptr
引用的地址可用于重新分配,但它尚未被重新分配,也没有任何东西可以覆盖该内存。 程序继续运行的时间越长,初始化更多的局部变量并调用malloc()
,则该内存地址被重用的可能性就越大,并且该值将发生变化。
但是,C规范中没有任何内容说明必须是这种情况-实现可以在范围超出范围时立即将该地址清零,或者在尝试读取它时使运行时出现恐慌,或者好吧,这就是“未定义”是什么意思。
作为程序员,您应注意避免这样做。 很多时候,它会引起的错误会很刺眼,但是在某些时候,您会导致间歇性的错误,这是最难追踪的错误。
在Java中,虽然超出范围的内存可能仍包含72
,但实际上无法访问它,因此不会影响程序员。 在Java中访问它的唯一方法是对它进行“正式”引用,在这种情况下,它不会被标记为垃圾回收,并且也不会超出范围。
Java中的原始类型放在堆栈中(放入frame的局部变量数组中)。 每次调用方法时都会创建一个新框架:
public void foo() {
int x = 72; // 'x' will be stored in the array of local variables of the frame
}
当框架的方法调用完成时,该框架将被销毁。 此时,所有局部变量和部分结果可能仍驻留在堆栈中,但是它们已被放弃并且不再可用。
我不是在看副书,但我猜这在技术上没有定义。
实际上,我曾经在C ++中尝试过一次类似的事情,但如果我没记错的话,实际上是72
(或在函数调用返回之前放置的任何东西),因此机器实际上并没有经过写0
到该位置或的东西。
其中一些也是实现细节。 我也用MIPS汇编语言实现了它(如果可以理解的话,我将包括一个代码示例)。 基本上,当我需要寄存器时,我只是通过所需的许多局部变量来“增加”堆栈,将所需的当前值存储在所需的寄存器中(以便以后可以恢复它们),然后重新使用寄存器。 如果是这样的实现,则该值实际上可以包含调用方中的局部变量的值。 不过,我认为这并不是Java所做的。
TL; DR这是一个实现细节,但是至少在C语言中,直到真正需要它时,它才会覆盖内存中的值。 Java很难预测。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.