初窺.NET C#中的 垃圾回收機制(一)

初窺.NET C#中的 垃圾回收機制 第一篇

雖然在 .NET Framework 中我們不太需要擔心記憶體管理的問題,但對於垃圾回收(Garbage Collection)機制的瞭解將有助於調校程式的效能,尤其是在進行大量簡訊發送時,即使是些微的效能落差也會影響使用者體驗。

垃圾回收機制 並不是近年的產物,此概念最早出現在約 1959 年,由 John McCarthy 建構的 LISP 語言中,然而 LISP 並不是一個成功的語言, 垃圾回收機制 沉寂了好一段時間,後來才在 1990 年代初期由 JAVA 發揚光大。儘管有部分開發者主張 垃圾回收機制的存在增加了電腦運行的負擔,但隨著硬體規格的上升,這方面的效能負擔已經逐漸不再是開發過程中的主要考量,反而因此機制的存在,開發者不再需要擔心未手動清除的記憶體內容可能會造成的不可預期問題,進而增進開發的效率,以下我們將介紹在 .NET C# 中的 垃圾回收機制 是如何運作。

 

在正式談到 垃圾回收機制 之前,我們先對.NET的記憶體存放區和機制進行簡單的介紹。.NET 運行時記憶體存放區可分成兩塊,分別是 Stack 及 Heap,前者會進行自我記憶體管理,每當最頂端的內容 pop 出去後便自動進行清理,不受垃圾回收機制影響,例如以下的加法函式在記憶裡存放的方式如右圖所示:

 

程式範例 回收機制說明

而Heap 類型的記憶體則須倚賴垃圾回收機制進行記憶體管理。而在 C# 語言中,Value type 的變數基本上交由 Stack 存放,Reference type 的變數則交由 Heap 存放;但此原則在某些狀況下存在例外,例如當 Value type 變數宣告在 Reference type 中時,便會一併放在 Heap 存放區中。

 

 

舉一個需仰賴垃圾回收機制的例子,首先我們依照上一個例子,如法炮製地宣告一個新函式,但這次的函示型別是我們自定義的一個 Reference type Class:

程式範例 機制說明

NumInt class 會被存放在 Heap 的記憶體區域,IncreaAdd 函式存放在 Stack 中,但其返回的結果屬於 NumInt 型別,也就是會指向 Heap 中的 NumInt class。此時要注意的是,如同我們在前面提到的,Stack 存放區會遵照 FIFO (First In Last Out)的原則進行 pop 的動作,自行進行記憶體清理——意即在這段程式執行完成之後,Stack 會被清空;然而 Heap 中的內容不會自行清空,若之後的程式再也沒用到 NumInt class,Heap 中的這段內容便會變成孤兒,佔據記憶體空間。這樣的孤兒數量一增加,便有可能造成記憶體的浪費、Memory Leak 的狀況,而垃圾回收機制在這種時候便能派上用場。

 

在 .NET 及 CLR 中,垃圾回收機制會監控每個物件的使用狀況,自 Root 開始掃描每個物件,能在路徑上被追蹤的物件被標記為 Live,每一輪的追蹤結束之後,未被標記為 Live 的物件便會被清理掉,這便是 Mark-Sweep 演算法。冗餘的物件被回收之後,記憶體空間便的不連續,此時使用 Mark-Compact 演算法對記憶體內容進行重新排序、壓縮,以增加讀取速度,中間還牽涉到許多指標之間的指向復原等等動作,更詳細的演算法內容及步驟我們將在下個篇章提到。


發表迴響

這個網站採用 Akismet 服務減少垃圾留言。進一步瞭解 Akismet 如何處理網站訪客的留言資料