這篇的核心問題其實是:為什麼明明把物件設成 null,甚至手動呼叫 GC.Collect(),解構函式看起來還是不一定立刻執行?
答案不是「GC 壞掉了」,而是很多人把 C# 的解構函式當成 C++ destructor 在理解。這兩者完全不是同一種生命週期模型。
先把名詞說清楚
C# 裡寫成:
~MyType()
{
Console.WriteLine("finalizer called");
}
語法上常被叫做解構函式,但它實際上對應的是 Finalize。也就是說:
- 它不是作用域結束就立刻執行
- 它不是 deterministic cleanup
- 它是 GC 流程中的最後保險機制
所以第一個結論很重要:
- finalizer 不應該被用來承擔「準時釋放資源」這種責任
有 finalizer 的物件,回收流程本來就更慢
當一個物件帶有 finalizer,GC 第一次發現它不可達時,不會直接把它記憶體回收掉,而是大致經過這種流程:
- 先判斷物件已不可達
- 把它排入 finalization 流程
- 由 finalizer thread 之後呼叫
Finalize - 下一輪 GC 才真正回收記憶體
這也是為什麼:
- 有 finalizer 的物件通常至少要多活一輪 GC
- finalizer 本身會拖慢回收
- 不必要的 finalizer 會增加 GC 壓力
為什麼把變數設成 null 還是不一定看到 finalizer
原因通常不是 null 沒效果,而是 JIT 與變數存活範圍(liveness)比你想像得更寬。
如果物件變數仍在目前方法的有效作用域內,JIT 可能仍把它視為活著。對 GC 來說,重點不是你「主觀上覺得不用了」,而是執行時是否仍可能從某處找到它的參考。
這也是為什麼在 Main 裡做實驗時,常會出現看似奇怪的現象:


你以為參考已經丟掉了,但對執行器來說,該區域仍未真正結束,物件仍可能被視為存活。
作用域真的會影響你看到的結果
如果物件建立在 Main 這種幾乎與整個程序同壽的作用域裡,那它常常要等到方法真正結束,甚至程序接近收尾時,你才比較容易觀察到 finalizer 執行。
但如果把物件封進一個較短生命週期的方法中,情況就不同了:
static void Main()
{
DoStuff();
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
}
static void DoStuff()
{
var obj = new Demo();
}
當 DoStuff() 返回後,局部參考更容易真正離開作用域,這時候你再觸發 GC,通常就比較能觀察到 finalizer。
對應到原來的實驗圖,大致就是這種情況:

GC.Collect() 也不是「立刻幫你跑完 finalizer」
很多人以為:
GC.Collect()= 立即回收所有垃圾
這其實不準確。若你只是單獨呼叫 GC.Collect(),對帶 finalizer 的物件來說,常常只會把它送進 finalization 流程,還沒真正釋放。
如果你真的只是為了做實驗觀察,常見測試寫法會是:
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
含義是:
- 第一次 collect 找出可回收物件
- 等待 finalizer thread 把 pending finalizer 跑完
- 第二次 collect 回收那些 finalizer 已完成的物件
但這只是觀察用技巧,不是一般業務程式的常規做法。
真正該怎麼釋放資源
如果型別持有的是非受控資源,或包住昂貴的可釋放資源,正確模型應該是:
- 主要靠
IDisposable - 需要時實作 finalizer 當保底
- 在
Dispose()中呼叫GC.SuppressFinalize(this)
也就是標準 dispose pattern,而不是期待 finalizer 幫你準時收尾。
這件事在 Unity / 遊戲開發尤其要小心
在遊戲或即時系統裡,如果把資源釋放責任交給 finalizer,通常會很糟,因為:
- 時機不可預測
- 額外增加 GC 壓力
- 很難控制 frame-time 抖動
所以實務上更應該主動管理生命周期,而不是被動等 GC。
實務上該記住什麼
- C# 的
~TypeName()是 finalizer,不是 C++ 式 destructor - finalizer 何時執行不可預測
- 帶 finalizer 的物件通常至少多活一輪 GC
- 變數是否真的離開作用域,會影響你能不能觀察到 finalizer
GC.Collect()不是日常資源管理工具- 真正需要準時釋放時,請用
IDisposable