我有一個函數彈出堆棧時內存會發生什麼變化?
public void f() {
int x = 72;
return;
}
所以x
存儲在可能的地址0x9FFF
。
當函數返回時,內存在那個地址會發生什麼?它還在嗎? I.e的值仍然是72
?還是完全無效?
我有一個函數彈出堆棧時內存會發生什麼變化?
public void f() {
int x = 72;
return;
}
所以x
存儲在可能的地址0x9FFF
。
當函數返回時,內存在那個地址會發生什麼?它還在嗎? I.e的值仍然是72
?還是完全無效?
我不是在不注意規格,但我猜這不是技術上的定義。
其實我試過類似的東西在C++中一次,它是,事實上,72
(或不管我放在那裏的函數調用返回前)如果我沒有記錯,所以該機並沒有真正經歷寫0
到那個位置或者什麼。
這也是一些實現細節。我也用MIPS彙編語言實現了它(如果我能挖掘它,我會包含一個代碼示例)。基本上,當我需要寄存器時,我只需通過我需要的很多局部變量來「增長」堆棧,將所需的寄存器中的當前值存儲爲任何值(以便稍後可以恢復它們),並重新使用寄存器。如果這是實現,則該值實際上可以包含調用者中的本地變量的值。不過,我不認爲這正是Java所做的。
TL; DR這是一個實現細節,但在C中,至少它不會覆蓋內存中的值,直到它真正需要它爲止。 Java很難預測。
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規範中沒有任何說明必須是這種情況 - 實現可能爲爲0,只要它超出範圍,或者在嘗試讀取時遇到運行時恐慌,或者,好吧,任何事 - 這就是「未定義」的意思。
作爲程序員,您應該小心避免這樣做。很多時候它會導致錯誤,但有些時候你會導致間歇性的錯誤,這是最難追查的。
在Java中,雖然內存在超出範圍後仍可能包含72
,但實際上沒有辦法訪問它,所以它不會影響程序員。在Java中可以訪問的唯一方式是,如果有「官方」引用,那麼它不會被標記爲垃圾回收,並且不會超出範圍。
總體回答良好。但是當你說「在Java中......如果有官方引用它時」,這是不可能的,因爲原始的int不是引用。 – Nayuki
基本類型中爪哇被放置在棧上(成frame的局部變量數組)。每次調用方法時都會創建一個新框架:
public void foo() {
int x = 72; // 'x' will be stored in the array of local variables of the frame
}
框架在其方法調用完成時被銷燬。此時所有局部變量和部分結果可能仍駐留在堆棧上,但它們被放棄並不再可用。
爲什麼'C'標籤? ''return;'也是不必要的。 – pzaenger
在C語言中,對於大多數體系結構而言,直到另一個代理覆蓋它(即ISR)時,該值仍然存在。在Java中,VM可以用許多不同的方式實現堆棧,並且JIT不必將VM堆棧轉換爲本地堆棧。所以,從Java的角度來看,它是失敗的。 –
@MargaretBloom好的信息,實際上可以添加爲答案,這比我的回答更好:) – EJoshuaS