跳至內容

Ruby 駭客指南/垃圾回收

來自 Wikibooks,開放的書,為開放的世界

程式的執行時結構

[編輯 | 編輯原始碼]

在深入本章內容之前,讓我們回顧一下程式執行時記憶體的組織方式。本章將涉及計算機體系結構的一些底層元件,因此事先熟悉一些基本概念是必要的。此外,這些概念也將在後面的章節中用到。讓我們在這裡把它們解決掉。

大多數 C 程式在它們的記憶體空間中都有以下元件

  1. 文字區域,
  2. 靜態和全域性變數的儲存,
  3. 機器棧,
  4. 堆。

文字區域是儲存程式碼的地方。第二個元件應該很清楚。機器棧是存放函式的引數和區域性變數的地方。堆是透過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()的實現。

現在讓我們開始本章的主題,垃圾回收。

介紹 GC

[編輯 | 編輯原始碼]

GC 做了什麼

[編輯 | 編輯原始碼]

標記 & 清除

[編輯 | 編輯原始碼]

清除 & 複製

[編輯 | 編輯原始碼]

引用計數

[編輯 | 編輯原始碼]

物件管理

[編輯 | 編輯原始碼]

struct RVALUE

[編輯 | 編輯原始碼]

物件堆

[編輯 | 編輯原始碼]

rb_newobj()

[編輯 | 編輯原始碼]

rb_gc_mark()

[編輯 | 編輯原始碼]

rb_gc_mark_children()

[編輯 | 編輯原始碼]

暫存器

[編輯 | 編輯原始碼]

mark_locations_array()

[編輯 | 編輯原始碼]

is_pointer_to_heap()

[編輯 | 編輯原始碼]

暫存器視窗

[編輯 | 編輯原始碼]

機器棧

[編輯 | 編輯原始碼]

Init_stack()

[編輯 | 編輯原始碼]

STACK_END

[編輯 | 編輯原始碼]

rb_gc_mark_locations()

[編輯 | 編輯原始碼]

其他根物件

[編輯 | 編輯原始碼]

特殊的 NODE 處理

[編輯 | 編輯原始碼]

終結器

[編輯 | 編輯原始碼]

rb_gc_force_recycle()

[編輯 | 編輯原始碼]

注意事項

[編輯 | 編輯原始碼]

記憶體釋放

[編輯 | 編輯原始碼]

GC 世代

[編輯 | 編輯原始碼]

GC 中的 volatile 關鍵字

[編輯 | 編輯原始碼]

初始化程式碼流

[編輯 | 編輯原始碼]

gc.c 內部

[編輯 | 編輯原始碼]

直譯器內部

[編輯 | 編輯原始碼]

物件建立

[編輯 | 編輯原始碼]

分配框架

[編輯 | 編輯原始碼]

使用者定義的物件建立

[編輯 | 編輯原始碼]

Data_Wrap_Struct()

[編輯 | 編輯原始碼]

Data_Get_Struct()

[編輯 | 編輯原始碼]

分配框架問題

[編輯 | 編輯原始碼]

<hline>

評論、建議和批評可以傳送到 Aoki MINERŌ <aamine@loveruby.net>。 請將翻譯評論、建議和批評傳送到本章譯者 mitcho (Michael Yoshitaka Erlewine) <mitcho@mitcho.com>。

華夏公益教科書