Ruby 駭客指南/垃圾回收
在深入本章內容之前,讓我們回顧一下程式執行時記憶體的組織方式。本章將涉及計算機體系結構的一些底層元件,因此事先熟悉一些基本概念是必要的。此外,這些概念也將在後面的章節中用到。讓我們在這裡把它們解決掉。
大多數 C 程式在它們的記憶體空間中都有以下元件
- 文字區域,
- 靜態和全域性變數的儲存,
- 機器棧,
- 堆。
文字區域是儲存程式碼的地方。第二個元件應該很清楚。機器棧是存放函式的引數和區域性變數的地方。堆是透過malloc()分配的。
讓我們詳細討論一下機器棧。被稱為機器“棧”,它顯然具有類似棧的結構。換句話說,我們可以不斷地向頂部新增新元素。在實踐中,值以單獨的int單元新增到堆疊中,但在概念上有一個稱為堆疊幀的較大單元。
每個堆疊幀對應一次函式呼叫。換句話說,每個函式呼叫都會新增一個堆疊幀,並且在return時會移除一個堆疊幀。如果我們極度簡化這一點,機器棧可能看起來像圖 1。
the top +-------------+ | stack frame | <-- the frame for the currently running function +-------------+ | stack frame | +-------------+ | stack frame | +-------------+ | stack frame | +-------------+ | stack frame | +-------------+ the bottom
圖 1:機器棧
在這張圖中,我們標記了棧的極端端為“頂部”,但機器棧並不一定從低到高地定址幀。例如,在 x86 機器上,棧從較高地址向較低地址增長。
使用malloc()可以分配任意大小的記憶體。alloca()是機器棧的版本。但是,透過alloca()分配的記憶體不需要釋放。或者,更確切地說,可以更好地說是記憶體隨著函式的return而“被”釋放。因此,alloca()分配的值不能用作函式的return值。這與“不能返回指向區域性變數的指標”相同。
所有這一切都很好。它基本上意味著我們可以本地分配長度動態變化的陣列。
但是,有一些環境沒有本機alloca()。許多人會希望在這些環境中也使用alloca(),因此可以用 C 編寫具有相同行為的函式。但是,在這種情況下,它可能只被實現為“不需要釋放”,但可能不一定會分配機器棧上的記憶體。事實上,它通常不會這樣做。如果它能夠做到這一點,那麼可能也存在alloca()的本機實現。
我們如何在 C 中實現alloca()?最直接的實現首先使用malloc()分配記憶體。然後,它將呼叫者函式和分配的地址儲存在全域性列表中。然後,下次呼叫alloca()時,如果存在為已經結束的函式分配的任何記憶體,就可以將其free()掉(見圖 2)。
+-----------+ +------------+ | main | | main | +-----------+ +------------+ | A | ===> | A | +-----------+ +------------+ | B | | B | mark that B -> alloca(32) +-----------+ | alloca(32) | free the memory allocated for D | C | +------------+ +-----------+ | D | | alloca(8) | mark that D -> alloca(8) +-----------+
圖 2:C 實現的alloca()的行為
Ruby 的missing/alloca.c就是這樣一個模擬alloca()的實現。
現在讓我們開始本章的主題,垃圾回收。
<hline>
評論、建議和批評可以傳送到 Aoki MINERŌ <aamine@loveruby.net>。 請將翻譯評論、建議和批評傳送到本章譯者 mitcho (Michael Yoshitaka Erlewine) <mitcho@mitcho.com>。