[英]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.