簡體   English   中英

退出方法時堆棧會發生什么?

[英]What happens to the stack when exiting a method?

我正在閱讀堆棧和堆什么以及在哪里? . 我有點模糊的一件事是方法退出后堆棧會發生什么。 以這張圖片為例:

堆

退出方法時堆棧被清除,但這意味着什么? 堆棧中的指針是否剛剛移回堆棧的開頭使其為空? 我希望這不是一個太寬泛的問題。 當堆棧從退出方法中清除時,我不太確定幕后發生了什么。

調用方法時,局部變量位於堆棧中。 對象引用也存放在棧中,對應的對象存放在堆中。

堆棧只是一個內存區域,它有一個起始地址和結束地址。 JVM(java 虛擬機)有一個指向當前棧頂(棧指針)的寄存器。 如果調用了一個新方法,則會在寄存器中添加一個偏移量以在堆棧上獲得新空間。

當方法調用結束時,堆棧指針將減少此偏移量,從而釋放分配的空間。

局部變量和其他東西(如返回地址、參數...)可能仍在堆棧中,並將被下一個方法調用覆蓋。

順便說一句:這就是 java 將所有對象存儲在堆中的原因。 當一個對象位於堆棧上,並且您將返回指向堆棧的引用時,該對象可能會被下一個方法調用銷毀。

在函數執行期間,所有局部變量都在堆棧中創建。 這意味着堆棧會增長,以便為這些變量留出足夠的空間。

當函數結束時,所有局部變量都離開作用域,堆棧被倒帶。 沒有其他事情需要發生,沒有隱式歸零內存。 但 :

  • 從語義上講,變量超出范圍,不能再使用
  • 以困難的方式,堆棧指針被倒回,有效地釋放內存:它將被下一個函數調用使用

以上不僅適用於函數,而且適用於任何代碼塊,因為從語義上講,塊中定義的變量在塊末尾超出范圍。

請記住,堆棧是分配給進程的內存區域。

總而言之,當您在代碼中調用函數時(通常是在匯編語言中),您需要將要使用的寄存器存儲在內存中(如果您遵循另一個合同,它可能會有所不同),因為這些寄存器可能會被覆蓋通過調用另一個函數(您需要存儲返回地址、參數等等,但讓我們省略它)。 為此,您將堆棧指針減少該數量的寄存器。 在退出之前,您需要確保將堆棧指針增加相同的數字。 您無需再執行任何操作,因為不再需要您存儲的值,它們將被下一個函數調用覆蓋。

在 Java 中,當對象本身在堆中時,對對象的引用就在堆棧中。 如果對某個對象的所有引用都從堆棧中刪除,垃圾收集器將從堆中刪除該對象。

希望我的回答對你有幫助。 另外, 檢查這個。

考慮編譯后的代碼在機器(或者,對我們人類來說更好,匯編)級別的外觀可能對您有用。 將此視為 X86 程序集中的一個可能示例:

調用該方法時,參數將在寄存器中傳遞或在堆棧本身上傳遞。 無論哪種方式,調用該方法的代碼最終都會:

call the_method

發生這種情況時,當前指令指針被壓入堆棧。 堆棧指針指向它。 現在我們在函數中:

the_method:
   push ebp
   mov  ebp, esp

當前基指針保留在堆棧上,然后基指針用於引用堆棧中的內容(如傳入變量)。

   sub  esp, 8

接下來,在堆棧上分配 8 個字節(假設分配了兩個四字節整數)。

   mov [ebp-4], 4
   mov [ebp-8], 2

分配了局部變量。 這實際上可以通過簡單地推動它們來實現,但更有可能涉及一個sub 快進到最后:

   mov esp, ebp
   pop ebp
   ret

發生這種情況時,堆棧指針會回到我們開始時的位置,指向存儲的基指針(保存的幀指針)。 這被彈回 EBP,讓 ESP 指向返回指針,然后用ret將其“彈出”到 EIP 中。 實際上,堆棧已展開。 盡管兩個局部變量的實際內存位置沒有改變,但它們實際上位於堆棧上方(物理上位於內存下方,但我想你明白我的意思。)

堆棧中的指針是否剛剛移回堆棧的開頭使其為空?

堆棧中的指針移回函數調用之前的位置。 堆棧不會為空,因為它包含屬於將程序帶到該點的調用的數據。

舉例說明:如果 func1 調用 func2 調用 func3,堆棧將如下所示:

func1 參數/本地變量... | func2 參數/本地變量... | func3 參數/本地變量...

func3 返回后,它將是:

func1 參數/本地變量... | func2 參數/本地變量...

堆棧就是一堆東西,通常是一堆幀,其中的幀包含參數、局部變量和對象實例,以及其他一些取決於您的操作系統的東西。

如果您在堆棧上實例化了對象,即 MyClass x 而不是 MyClass * x = new MyClass(),那么當堆棧返回到前一幀時,對象 x 將被拆除並調用其析構函數,這實質上只是使當前堆棧指針(內部)指向前一幀。 在大多數母語中,不會清除內存等。

最后,這就是為什么您應該初始化局部變量(在大多數語言中),因為對下一個函數的調用將設置一個新框架,該框架很可能與先前重繞的堆棧框架位於同一位置,因此您的局部變量將包含垃圾。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM