GC 根引用類型
| 根類型 | 示例 |
| 靜態字段 | static List _cache |
| 線程棧變量 | 方法局部變量、參數 |
| CPU 寄存器 | JIT 產生的臨時引用 |
| GC 句柄 | GCHandle.Alloc(obj) |
| 終結器隊列對象 | 有 ~Finalize() 的對象 |
標記-壓縮算法流程
- 標記:從根集合出發,遞歸遍歷所有可達對象,標記為「存活」。
- 壓縮:將存活對象向堆起始端移動,消除空洞,更新所有引用指針。
- 分配:新對象在壓縮後的空閒空間連續分配(指針碰撞),極快。
終結器機制詳細流程
class Obj { ~Obj() { /* 清理 */ } }
// 1. 創建 Obj 實例 → 進入「終結器隊列」
// 2. GC 標記階段發現 Obj 不可達,但有終結器
// 3. Obj 從終結器隊列移除,放入「F-reachable 隊列」
// 4. 專用 Finalizer 線程執行 Obj.Finalize()
// 5. 執行完成 → Obj 變為普通不可達對象
// 6. 下次 GC 循環真正回收內存
終結器代價
- 對象至少經歷兩次 GC 才能回收(第一次進 F-reachable,第二次真正回收)。
- Finalizer 線程單線程順序執行,阻塞會延遲所有終結器。
- 終結器中拋出異常會終止進程(.NET 2.0+)。
替代方案:IDisposable + using
- 應用層主動釋放,對象無需進入終結器隊列,單次 GC 即可回收。
- 性能提升顯著,語義清晰。
代碼演示:終結器 vs IDisposable
// 終結器版
class WithFinalizer { ~WithFinalizer() { Console.WriteLine("Finalized"); } }
// IDisposable 版
class WithDispose : IDisposable
{
public void Dispose() { Console.WriteLine("Disposed"); GC.SuppressFinalize(this); }
~WithDispose() { Dispose(); } // 保底
}
// 測試
using (var d = new WithDispose()) { } // 立即輸出 Disposed,無終結器開銷
var f = new WithFinalizer(); f = null; GC.Collect(); GC.WaitForPendingFinalizers(); // 輸出 Finalized,延遲兩次 GC
- 上一篇文章: C# 內存管理深度解析(九):IDisposable 模式與 using 塊
- 下一篇文章: C# 內存管理深度解析(七):ICloneable 深拷貝與字符串特例
Copyright ©
程式碼之家 All Rights Reserved