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

深入理解 C# 記憶體管理:堆疊、堆積、垃圾回收與最佳實務

     編輯:關於C#

雖然 .NET 執行期(CLR)提供自動記憶體管理,但理解堆疊與堆積的運作機制、值類型與參考類型的儲存差異、垃圾回收器(GC)的代演算法,以及 IDisposable 模式,對撰寫高效能、無記憶體洩漏的程式至關重要。本文系統整理核心觀念與實務技巧。

堆疊 vs 堆積

特性堆疊堆積
資料結構LIFO(後進先出),像疊盤子動態分配,可隨機存取
存放內容方法呼叫框架、區域變數(值類型)、參數、回傳位址參考類型實例(物件)、陣列、字串、boxed 值類型
生命週期方法結束自動釋放由 GC 追蹤根參考,非預期性回收
效能極快(指標移動)較慢(分配、壓縮、代檢查)

類型分類與儲存位置

  • 值類型(int、struct、enum、bool 等):預設儲存在宣告處的堆疊;若為參考類型的欄位,則隨物件在堆積。
  • 參考類型(class、interface、delegate、string、陣列):物件本體在堆積,變數(參考)在堆疊。
  • 指標:CLR 內部管理,unsafe 區塊可顯式操作。
  • 指令:JIT 編譯後的機器碼,儲存於程式碼堆疊。

參數傳遞機制

// 傳值:複製值類型內容(大型 struct 效能差)
void AddFive(int pValue) { pValue += 5; }

// 傳參考:複製參考(指標),雙方指向同一物件
void Modify(MyClass obj) { obj.Prop = 10; }

// ref/out 傳值類型參考:避免大型 struct 複製
void DoSomething(ref MyStruct s) { ... }

// ref 傳參考類型:可替換呼叫端的物件參考
void Switcharoo(ref Thing t) { t = new Vegetable(); }

淺複製 vs 深複製與 ICloneable

參考類型成員若僅複製參考,會導致雙方共用同一子物件(如 Shoe 範例)。實作 ICloneable 並在 Clone() 中遞迴複製所有參考成員,才能獲得真正獨立的深複製。

垃圾回收器(GC)核心機制

  1. 根列舉:靜態欄位、堆疊區域變數、CPU 登錄器參考。
  2. 標記階段:從根遞迴追蹤所有可達物件,建立物件圖。
  3. 壓縮階段:移動存活物件消除碎片,更新所有參考指標。
  4. 代機制:Gen 0(短命)、Gen 1(緩衝)、Gen 2(長命/大物件堆 LOH)。新物件多在 Gen 0 分配,倖存者晉升下一代。

釋放非受控資源:Finalizer vs IDisposable

  • Finalizer(~Class()):GC 呼叫,非決定性、延遲、需兩次 GC 週期、效能負擔大。僅作為安全網。
  • IDisposable.Dispose():開發人員決定性呼叫,配合 using 區塊(編譯為 try/finally),立即釋放檔案、資料庫連線、GDI 物件等。
  • 標準模式:實作 Dispose(bool disposing),Dispose() 呼叫 Dispose(true) 並 GC.SuppressFinalize(this);Finalizer 呼叫 Dispose(false)。

效能優化關鍵原則

  1. 盡早釋放非受控資源:using 區塊、try/finally 確保 Dispose() 執行。
  2. 減少長壽命物件參考:靜態集合、事件未移除、Singleton 持有大量資料,會阻礙 GC 回收。
  3. 避免不必要的 Finalizer:只有直接持有非受控資源才需實作。
  4. 大物件(>85KB)直入 LOH:LOH 不壓縮,易碎片化;重複分配大陣列/字串建議用 ArrayPool、StringBuilder、MemoryStream 重用。
  5. ValueTuple、readonly struct、ref struct:減少堆積分配、避免防禦性複製。

常見陷阱

  • 靜態事件處理器未移除 → 發布者長期持有訂閱者參考 → 記憶體洩漏。
  • List.Clear() 僅移除元素,容量不釋放;需 list = new List() 或 TrimExcess()。
  • 大量字串串接 → 產生大量暫時字串 → Gen 0 壓力大 → 使用 StringBuilder。
  • Task/async 狀態機捕獲區域變數 → 延長生命週期 → 注意閉包捕獲。

深入掌握記憶體模型,不只是面試題,更是寫出穩健、高效 .NET 應用程式的基石。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved