Dispose() do not free memory instantly
.NET 中有自動記憶體管理機制,稱為 GC 。在下的觀念還不是很清楚,所以這篇不敢談太多背後的原理 :p
GC 讓 Programmer 不能直接管理記憶體,在我看來這應該能稱作「將記憶體管理抽象化」。 GC 並不會在「將變數設為 NULL」或「呼叫某物件的 Destructor」時馬上歸還被佔用的記憶體,而會等到某個不明的條件1發生時才啟動回收機制。但也並不是說將變數設為 NULL 或呼叫 Destructor 完全沒用,當某個條件發生時,這些變數、物件所佔用的記憶體便會被釋出。
為了確保 Desturctor 釋放出該釋放的資源,我們可以實作 IDisposable 介面:
private bool disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
// Release managed resources
}
// Release unmanaged resources
}
disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~YourClass()
{
Dispose(false);
}
實作了 IDisposable 介面之後,便可以在使用完物件時呼叫 obj.Dispose() 或者是使用 using 來將資源標記為「下次可以被釋放」。 using 的用法如下,這種寫法可以讓 Dispose() 自動地被呼叫:
using(YourClass obj = new YourClass())
{
// Do something
}
除了 Dispose() 以外, C# 中還有一個名為 Finalize() 的 Method ,但這個 Method 無法被覆寫,所以我沒有多加研究。
不過就算照著上面作完了,記憶體仍然必須等到某個條件發生時才會釋放,如果那個條件一直沒有達到,程式就還是會發生「記憶體不足」的問題。
To GC.Collect(), or not to GC.Collect()
有許多文章說,用 GC.Collect() 可以確實地釋放出記憶體。這裡有人做了實驗,他先將記憶體塞滿,當 OutOfMemoryException 發生時,便呼叫 GC.Collect(GC.MaxGeneration) 強制 GC 釋放資源,另有一篇文章建議寫成,不過根據他測試的結果,這還必須跟 gcServer 這個屬性配合才行。組態檔的寫法如下:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<runtime>
<gcServer enabled="false" />
</runtime>
</configuration>
不過 gcServer 該設成 true 或 false 則沒有明說。另有一篇文章提到,如果電腦的 CPU 是多核心,則 .NET 預設會採用 true ,也就是 Server Mode 。這種模式下記憶體達到釋放的門檻較高,容易發生門檻還未達到,記憶體便用罄的窘境。設成 false ,使用 Workstation Mode 可獲得改善。然而這篇文章談的是 ASP.NET ,我不確定在 Windows Form 應用程式中這個規則是否也能適用。
如果你寫的是 ASP.NET ,組態檔放在 %WINDIR%\Microsoft.NET\Framework\(版本)\Aspnet.config 。而 Windows Form 的組態檔則視執行檔檔名而定,若你的執行檔檔名是 hello.exe ,組態檔就放在同目錄下的 hello.exe.config 。
雖然 GC.Collect() 可能能夠解決問題,但必須注意不能過度使用,以免程式效能低落。確定要用 GC.Collect() 的話,有一篇文章提出了建議的寫法:
GC.Collect();
GC.WaitForPendingFinalizer();
GC.Collect();
-
對我來說是滿神秘的啦~ XD ↩