避免使用終結器 如果沒有必要,是不需要實現一個終結器(Finalizer)。終結器的代碼主要是讓GC回收非托管資源用。它會在GC完成標記對象為可回收後,放入一個終結器隊列里,在由另外一個線程執行隊列里對象的終結器方法。這就意味著,如果你實現一個類的終結器,你必須保證在它在終結器執行後能被正常回收。這 ...
避免使用終結器
如果沒有必要,是不需要實現一個終結器(Finalizer)。終結器的代碼主要是讓GC回收非托管資源用。它會在GC完成標記對象為可回收後,放入一個終結器隊列里,在由另外一個線程執行隊列里對象的終結器方法。這就意味著,如果你實現一個類的終結器,你必須保證在它在終結器執行後能被正常回收。這需要消耗一些CPU資源在清理對象上,會極大降低GC的整體效率。
如果你實現一個終結器,你也必須實現一個IDisposable介面用來清理資源,併在Dispose方法里調用GC.SupperessFinalize(this)將對象從終結器隊列里移除。如果你在下次回收之前,正確的調用了Disspose方法,就不會讓終結器執行。下麵的慄子就是正確的演示了這個模式。
class Foo : IDisposable
{
~Foo()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
this.managedResource.Dispose();
}
// Cleanup unmanaged resourced
UnsafeClose(this.handle);
// If the base class is IDisposable object
// make sure you call:
//base.Dispose(disposing);
}
}
你可以通過 [http://www.writinghighperf.net/go/15] 獲得過的關於Dispose模式與終結器的更多信息。
註意 有些人認為終結器一定會被執行。正常情況下沒錯,但這不是絕對的。如果一個程式被強制終止,那麼進程會立即消失,終結器自然也不會執行。當然主動退出時也許會有一個短暫的等待進程關閉時間,但如果你的終結器在終結器列表的後面,也是有可能不會被執行。此外由於終結器隊列是循序執行,如果某個終結器進入了死迴圈,那麼後面的終結器就不會被執行到。終結器不是執行在GC線程里,但他們會引發GC。
避免大對象
在分析了大量的程式代碼後,大對象的邊界被定義在85000 bytes上。任何大於等於這個邊界值的對象都會判定為“大對象”,需要分配在一個單獨的堆里。
我們希望儘可能的避免在大對象堆上進行分配。這不僅會導致更長的GC,也更容易造成記憶體碎片,讓記憶體的分配邊界隨著時間不對增加。
為了避免這些問題,你需要嚴格控製程序在大對象堆里分配內容。你需要統籌安排你的對象在應用程式生存周期里的分配方案。
LOH是不會自動壓縮的,但在.NET 4.5.1 之後,你還是可以通過特定方法去通知GC進行壓縮。然而,這個是你最後的手段,因為這將導致一次很長的暫停。在做這之前,你還是好好想想,應該如何避免進入這個情況。
避免複製緩衝區
如果可能,你應該儘量避免複製數據。例如:如果你打算將一個文件數據讀入MemoryStream(如果你需要一個大的緩衝區,最好做一個合併)。一旦分配了記憶體,每個組件都將從這個數據里的同一個副本里讀取數據,並將其視為開發準則。
如果你只需要使用這個緩衝區里的一部分,可以使用ArraySegment
var memoryStream = new MemoryStream();
var segment = new ArraySegment<byte>(memoryStream.GetBuffer(), 100, 1024);
var blockStream = new MemoryStream(segment.Array, segment.Offset, segment.Count);
複製記憶體最大的問題不在CPU而是GC。如果你發現要複製緩衝區,可以嘗試將其複製或合併到一個現有的緩衝區里,以避免任何新的記憶體分配。