有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java当堆栈弹出时内存会发生什么变化?

我有一个函数

public void f() {
    int x = 72;
    return;
}

因此x可能存储在地址0x9FFF

当函数返回时,该地址的内存发生了什么变化?它还在吗?也就是说,值是否仍然72?还是完全无效


共 (4) 个答案

  1. # 1 楼答案

    我不是随便看规范,但我猜这在技术上是没有定义的

    <>我实际上在C++中尝试过类似的东西,实际上是^ {CD1>}(或者我在函数调用之前放的任何东西)如果我没有记错的话,所以机器实际上没有经过并写入^ {CD2>}到那个位置或某物。

    其中一些也是实现细节。我也用MIPS汇编语言实现了它(如果我能找到它,我将包括一个代码示例)。基本上,当我需要寄存器时,我只需按我需要的本地变量的多少来“增长”堆栈,存储我需要的寄存器中的当前值(以便以后恢复它们),然后重新使用寄存器。如果这是实现,那么该值实际上可以包含调用者中的局部变量的值。不过,我不认为Java正是这么做的

    TL;DR这是一个实现细节,但在C语言中,至少在实际需要之前,它不会覆盖内存中的值。Java更难预测

  2. # 2 楼答案

    Java中的基元类型放在堆栈上(放在frame局部变量数组中)。每次调用方法时都会创建一个新框架:

    public void foo() {
        int x = 72; // 'x' will be stored in the array of local variables of the frame
    }
    

    当框架的方法调用完成时,框架将被销毁。此时,所有局部变量部分结果可能仍驻留在堆栈上,但它们已被放弃,不再可用

  3. # 3 楼答案

    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)上,堆栈内存也得到了特殊处理,因此增加堆栈总是会返回一个填充了零字节的区域,这节省了实际从内存加载旧值的工作

  4. # 4 楼答案

    在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中访问它的唯一方法是对它进行“官方”引用,在这种情况下,它不会被标记为垃圾收集,也不会真正超出范围