.NET 物件生命週期包含建立、使用、釋放三階段。理解記憶體管理機制,特別是非託管資源的正確釋放,是編寫高效能、無記憶體洩漏應用程式的關鍵。
宣告變數(FileStream fs)僅在堆疊配置參考位址。new FileStream(...) 觸發三步驟:
GC 自動回收不再被參考的物件,觸發時機:
GC.Collect()(不建議頻繁使用,會暫停執行緒)GC 回收前會檢查物件是否有 Finalize 方法(C# 寫作 ~ClassName()),有則先呼叫以釋放非託管資源。
FileStream 內部持有 SafeFileHandle 封裝 Win32 HANDLE。若未呼叫 Close(),檔案會被鎖定無法刪除,直到 GC 執行 Finalize 才會釋放。
FileStream fs = new FileStream(@"C:\test.txt", FileMode.OpenOrCreate);
Console.ReadLine(); // 此時嘗試刪除檔案會失敗
// 程式結束時 GC 才會釋放
FileStream fs = new FileStream(@"C:\test.txt", FileMode.OpenOrCreate);
// 使用檔案...
fs.Close(); // 立即釋放非託管資源
File.Delete(@"C:\test.txt"); // 成功
封裝非託管資源的類別應實作標準模式:
public class MyResourceWrapper : IDisposable {
private SafeHandle _handle; // 非託管資源
private bool _disposed = false;
// 公開給呼叫端明確釋放
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this); // 告訴 GC 不需再呼叫 Finalize
}
// 核心釋放邏輯
protected virtual void Dispose(bool disposing) {
if (!_disposed) {
if (disposing) {
// 釋放託管資源
}
// 釋放非託管資源
_handle?.Dispose();
_disposed = true;
}
}
// Finalize 保底機制
~MyResourceWrapper() {
Dispose(false);
}
}
| 方法 | 呼叫者 | 用途 |
|---|---|---|
| Finalize (~Ctor) | GC 自動呼叫 | 保底釋放非託管資源 |
| Dispose() | 開發者手動呼叫 | 及時釋放託管+非託管資源 |
| Close() | 開發者手動呼叫 | 通常等同 Dispose,語意更明確 |
using 區塊確保 Dispose 被呼叫IDisposable 時務必提供 Finalize 保底掌握 Dispose 模式,讓非託管資源「有借有還」,是 .NET 專業開發者的必修課。