相對於C++程序員來說,C#程序員是非常幸運的,至少我們不需要為內存洩漏(Memory Leak)而頭疼,不需要負責內存的分配和回收。但這不意味著我們只需要知道new的語法 就可以了,作為一個嚴肅的C#程序員,我們應該對此有所了解,有助於我們編寫性能更好 的代碼。
主要內容:
CLR的內存分配機制
CLR的回收機制
一、CLR的內存分配機制
.NET Framework 的垃圾回收器管理應用程序的內存分配和釋放。每次使用 new 運算 符創建對象時,運行庫都從托管堆為該對象分配內存。只要托管堆中有地址空間可用,運 行庫就會繼續為新對象分配空間。
...
object obj = new object();
...
但是,內存不是無限大的。
public void FillMemory()
{
ArrayList memory = new ArrayList();
// 輸出填充前所占內存大小
Console.WriteLine("used memory:" + GC.GetTotalMemory(false));
for (int i = 0; i < 100000; i++)
{
memory.Add(new object());
}
// 輸出填充後所占的內存大小
Console.WriteLine("used memory:" + GC.GetTotalMemory(false));
}
最終,垃圾回收器必須執行回收以釋放一些內存。垃圾回收器優化引擎根據正在進行 的分配情況確定執行回收的最佳時間。當垃圾回收器執行回收時,它檢查托管堆中不再被 應用程序使用的對象並執行必要的操作來回收它們占用的內存。
二、CLR的內存回收機制
一般我們在程序中創建的對象大部分都是托管對象,可依靠GC自動進行內存的回收, 但是對於封裝了非托管資源的對象,就需要我們顯式重載object.Finalize()接口來實現 非托管資源的釋放。
using System;
using System.IO;
public class Foo
{
private SomeComObject _com;
public Foo()
{
_com = new SomeComObject();
}
// some other operation here...
~Foo()
{
// release the unmanaged resource
_com.Close();
}
}
* 析構函數會在編譯時會被翻譯為protected void Finalize(),這是C#的析構函數的 語法
GC在回收對象之前會調用Finalize()來實現非托管資源的釋放,不過按照MSDN的說法 ,Finalize()會導致性能的降低。
“垃圾回收器使用名為“終止隊列”的內部結構跟蹤具有 Finalize 方法的對象。每次您的應用程序創建具有 Finalize 方法的對象時,垃圾回收器都在終止 隊列中放置一個指向該對象的項。托管堆中所有需要在垃圾回收器回收其內存之前調用它 們的終止代碼的對象都在終止隊列中含有項。
實現 Finalize 方法或析構函數對性能可能會有負面影響,因此應避免不必要地使用 它們。用 Finalize 方法回收對象使用的內存需要至少兩次垃圾回收。當垃圾回收器執行 回收時,它只回收沒有終結器的不可訪問對象的內存。這時,它不能回收具有終結器的不 可訪問對象。它改為將這些對象的項從終止隊列中移除並將它們放置在標為准備終止的對 象列表中。該列表中的項指向托管堆中准備被調用其終止代碼的對象。垃圾回收器為此列 表中的對象調用 Finalize 方法,然後,將這些項從列表中移除。後來的垃圾回收將確定 終止的對象確實是垃圾,因為標為准備終止對象的列表中的項不再指向它們。在後來的垃 圾回收中,實際上回收了對象的內存。” --[MSDN]
更加建議實現Sytem.IDisposable.Dispose()接口,用來實現對非托管資源的釋放,這 也是.Net Framework中常見的設計模式。那該怎麼實現Dispose呢?
1、首先,Dispose接口應該釋放自身對象所占用的資源,還應該調用基類的Dispose方 法,釋放基類部分所占用的資源。
public void Dispose()
{
// do something to release my unmanaged resource
ReleaseMyResource();
base.Dispose();
}
2、前面說過Finalize()會導致性能問題,那麼在執行Dispose以後就應該告訴GC不用 在調用Finalize()了
public void Dispose()
{
// do something to release my unmanaged resource
ReleaseMyResource();
base.Dispose();
// tell gc not to call Finalize()
GC.SuppressFinalize(this);
}
當然我們完全可以定義一個MySpace.IClosable.Close(),通過實現這個接口來進行非 托管資源的釋放,不過這實在沒有必要。