首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

.NET 深度學習筆記 (3):垃圾回收 (GC) 與記憶體管理核心機制實戰解析

     編輯:關於.NET

GC 是 .NET 執行環境的核心支柱,理解其運作原理、代碼分代策略、非托管資源處理模式,是寫出高效能、無記憶體洩漏程式的前提。本文從型別記憶體配置、GC 演算法、Dispose 模式到實戰測試代碼,完整建立記憶體管理知識體系。

.NET 型別與記憶體配置基礎

分類型別範例配置位置生命週期管理存取效能
值型別int, struct, enum, DateTimeStack (執行緒堆疊)作用域結束自動釋放 (LIFO)極高 (無間接參考)
參考型別class, string, array, delegateManaged Heap (託管堆)GC 追蹤根引用,不可達時回收需解參考 (Pointer dereference)

值型別堆疊配置示意圖

參考型別堆疊+堆配置示意圖

GC 核心演算法:分代回收 (Generational GC)

三代假設

  • 第 0 代 (Gen 0):新建立物件、短暫存活物件 (區域變數、暫存物件)
  • 第 1 代 (Gen 1):經過一次 Gen 0 回收仍存活的物件 (緩衝區)
  • 第 2 代 (Gen 2):長期存活物件 (單例、靜態欄位、快取資料)

回收觸發條件

  • Gen 0 空間不足 (分配指標超過臨界值)
  • 手動呼叫 GC.Collect() (強烈不建議生產環境使用)
  • 系統記憶體壓力大 (Windows 通知)

回收流程

  1. 標記階段:從 GC Roots (堆疊變數、靜態欄位、CPU 暫存器) 遞迴標記所有可達物件
  2. 壓縮階段:將存活物件向堆疊底部移動,消除記憶體碎片,更新所有參考位址
  3. 代碼提升:存活物件代碼 +1 (Gen 0→1, Gen 1→2)

效能優勢

  • 新物件分配僅移動指標 O(1),比 C++ malloc 快數量級
  • 分代策略避免每次掃描整個堆疊,Gen 0 回收極快 (通常 < 1ms)
  • 大型物件堆 (LOH, >85KB) 獨立管理,不壓縮避免移動開銷

非托管資源:GC 的盲區

GC 僅管理託管記憶體。封裝作業系統資源的物件屬非托管資源,GC 不知道如何釋放:

資源釋放雙軌制:Finalize vs Dispose

類別範例型別資源本質
檔案/流FileStream, StreamReader, StreamWriter檔案控制代碼
資料庫SqlConnection, OdbcConnection, OleDbDataReader連線池、網路通訊端
圖形/GDIBitmap, Graphics, Pen, Brush, Font, IconGDI 物件控制代碼
網路Socket, TcpClient, HttpWebRequest通訊端控制代碼
COM/InteropExcel.Application, Word.DocumentCOM 參考計數
同步/執行緒Mutex, Semaphore, Timer, WaitHandle核心物件控制代碼

標準 Dispose 模式完整實作 (Microsoft 推薦)

public class ResourceHolder : IDisposable
{
    private IntPtr _handle;           // 非托管資源控制代碼
    private Component _managed;       // 托管資源 (也實作 IDisposable)
    private bool _disposed = false;   // 防重複釋放旗標

    public ResourceHolder()
    {
        _handle = NativeMethods.OpenResource();
        _managed = new Component();
    }

    // 公開 Dispose:決定性釋放
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);  // 關鍵:從 Finalization Queue 移除,避免雙重清理
    }

    // 核心邏輯:disposing=true 來自 Dispose(),false 來自 Finalizer
    protected virtual void Dispose(bool disposing)
    {
        if (_disposed) return;

        if (disposing)
        {
            // 釋放托管資源 (安全:其他物件尚未被 Finalize)
            _managed?.Dispose();
        }

        // 釋放非托管資源 (無論 disposing 真偽都必須執行)
        if (_handle != IntPtr.Zero)
        {
            NativeMethods.CloseHandle(_handle);
            _handle = IntPtr.Zero;
        }

        _disposed = true;
    }

    // Finalizer:防禦性備援 (僅在 Dispose 未被呼叫時執行)
    ~ResourceHolder()
    {
        Dispose(false);  // 僅清理非托管資源
    }

    // 方法防護:防止使用已釋放物件
    public void DoWork()
    {
        if (_disposed) throw new ObjectDisposedException(nameof(ResourceHolder));
        // 實際工作...
    }
}

using 陳述式:語法糖保證釋放

// 等價於 try/finally 區塊
using (var resource = new ResourceHolder())
{
    resource.DoWork();
}
// 編譯器產生:
// try { resource = new ResourceHolder(); resource.DoWork(); }
// finally { if (resource != null) resource.Dispose(); }

IL 驗證:using 與手寫 try/finally Dispose() 產生完全相同的 IL 指令碼。

實戰測試代碼:四種資源類別行為對比

// 1. 實作 IDisposable + using (最佳)
using (var obj = new WithDispose()) { obj.DoWork(); } // 立即釋放

// 2. 無 Finalize (純托管/安全資源) - 依賴 GC
var obj2 = new NoFinalize(); obj2.DoWork(); // 不確定時機回收

// 3. 僅 Finalizer (舊版模式) - 雙重 GC 開銷
var obj3 = new WithDestructor(); obj3.DoWork(); // 兩次 GC 才釋放

// 4. 錯誤:Finalizer 類別不能用 using (編譯錯誤)
// using (var obj4 = new WithDestructor()) { } // CS1674: 不實作 IDisposable

關鍵最佳實踐總結

  1. 持有非托管資源的類別必須實作 IDisposable
  2. 公開 Dispose() 呼叫 GC.SuppressFinalize(this) 移除 Finalization 開銷
  3. Finalizer 僅作防禦備援,不可替代 Dispose
  4. 使用 using 區塊 確保區塊結束時釋放 (即使拋出例外)
  5. 避免在 Finalizer 存取其他參考型別欄位 (可能已被回收)
  6. 大型物件 (>85KB) 分配謹慎,進入 LOH 避免頻繁 Gen 2 回收
  7. 弱引用 (WeakReference) 實作快取,允許 GC 回收記憶體壓力下的資料

記憶體管理不是「有 GC 就不用管」,而是理解 GC 行為、配合決定性釋放模式、設計友善 GC 的物件圖。這是 .NET 開發者從初級邁向資深的核心能力分水嶺。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
機制觸發時機決定性效能影響適用場景
Finalizer (~ClassName)GC 回收前 (非決定性)❌ 不確定雙重 GC 週期、Finalizer 執行緒阻塞防禦性程式設計、最後防線
IDisposable.Dispose()程式碼明確呼叫 (決定性)✅ 確定無額外 GC 負擔所有非托管資源持有者