简体   繁体   中英

What happens to memory when stack is popped?

I have a function

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

So x is stored at possibly the address 0x9FFF .

When the function returns, what happens to the memory at that address? Is it still there? Ie is the value still 72 ? Or is it completely invalidated?

Both the Java programming language and the Java virtual machine do not define what happens to the memory of a stack frame after the frame is popped. This is a low-level implementation detail that is masked by the higher level abstractions. In fact, the Java language and JVM bytecode make it impossible by design to retrieve already-deleted values from the stack (unlike C/C++).

In practice however, stack frames in Java will behave like stack frames in C. Growing the stack will bump its pointer (usually downward) and allocate space to store variables. Shrinking the stack will usually bump the pointer up and simply leave the old values in memory to rot without overwriting them. If you have low-level access to the JVM's stack memory region, this is the behavior you should expect to see.

Note that it is impossible in Java to do a trick like C where you attempt to read uninitialized stack variables:

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

Other stack behaviors are possible in JVM implementations. For example, as frames are popped it is possible to unmap the 4 KiB virtual memory pages back to the operating system, which will actually erase the old values. Also on machine architectures such as the Mill , stack memory is treated specially so that growing the stack will always return a region filled with zero bytes, which saves the work of actually loading old values from memory.

In C it is undefined behaviour .

In practice, if you were to try something like:

 int *ptr;

 void foo() {
    bar();
    printf("%d", *ptr);
 }

 void bar() {
     int x = 72;
     ptr = &x;
 }

Then it's likely that in most implementations of C, foo() would print 72 . This is because although the address referenced by ptr is available for reallocation, it's not likely to have been re-allocated yet, and nothing has overwritten that memory. The longer your program continues to run, initialising more local variables, and calling malloc() , the more likely it is that this memory address will be re-used, and the value will change.

However there's nothing in the C specification that says this must be the case - an implementation could zero that address as soon as it goes out of scope, or make the runtime panic when you try to read it, or, well, anything -- that's what "undefined" means.

As a programmer you should take care to avoid doing this. A lot of the time the bugs it would cause would be glaring, but some of the time you'll cause intermittent bugs, which are they hardest kind to track down.

In Java, while it's possible that the memory still contains 72 after it goes out of scope, there is literally no way to access it, so it does not affect the programmer. The only way it could be accessed in Java would be if there were an "official" reference to it, in which case it would not be marked for garbage collection, and isn't really out of scope.

Primitive types in Java are placed on the stack (into a local variables array of a frame ). A new frame is created each time a method is invoked:

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

A frame is destroyed when its method invocation completes. At this moment all local variables and partial results might still reside on the stack, but they are abandoned and no longer available.

I'm not looking at the spec offhand, but I'm guessing that this isn't technically defined.

I actually tried something like that in C++ once and it was, in fact, 72 (or whatever I put there before the function call returned) if I remember correctly, so the machine didn't actually go through and write 0 to that location or something.

Some of this is an implementation detail, too. I implemented it in MIPS assembly language as well (I'll include a code sample if I can dig it up). Basically, when I needed registers, I'd just "grow" the stack by however many local variables I needed, store whatever the current values in the registers I needed were (so I could restore them later), and re-use the register. If that's the implementation, then the value could actually contain the value of a local variable in the caller . I don't think that that's exactly what Java's doing, though.

TL;DR It's an implementation detail, but in C at least odds are it won't overwrite the value in memory until it actually needs it. Java is much harder to predict.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM