簡體   English   中英

C#.NET垃圾收集無法正常運行?

[英]C# .NET Garbage Collection not functioning?

我正在Visual Studio 2010中開發一個相對較大的解決方案。它有各種項目,其中一個是XNA Game-project,另一個是ASP.NET MVC 2項目。

對於這兩個項目,我面臨同樣的問題:在調試模式下啟動它們后,內存使用率不斷上升。 它們分別以40和100MB的內存使用量開始,但兩者都相對較快地升至1.5GB(分別為10分鍾和30分鍾)。 之后它有時會回落到接近初始使用狀態,有時它會拋出OutOfMemoryExceptions

當然,這將表明嚴重的內存泄漏,所以這是我最初試圖發現問題的地方。 在不成功地搜索泄漏之后,我嘗試定期調用GC.Collect() (大約每10秒一次)。 在引入這個“黑客”后,內存使用量分別保持在45和120MB 24小時(直到我停止測試)。

.NET的垃圾收集應該是“非常好的”,但我不禁懷疑它只是不起作用。 我使用CLR Profiler試圖解決問題,它表明XNA項目似乎已經保存了很多我確實使用的字節數組,但是引用應該已經被刪除,因此被垃圾收集集電極。

同樣,當我定期調用GC.Collect() ,內存使用問題似乎已經消失。 有誰知道什么可能導致這種高內存使用? 它可能與在調試模式下運行有關嗎?

在不尋常地尋找泄漏之后

更努力=)

托管語言中的內存泄漏可能很難追蹤。 我對Redgate ANTS Memory Profiler有很好的經驗。 它不是免費的,但它們會為您提供為期14天的全功能試用版。 它有一個很好的UI,可以顯示內存分配的位置以及這些對象保存在內存中的原因。

正如Alex所說,事件處理程序是.NET應用程序中非常常見的內存泄漏源。 考慮一下:

public static class SomeStaticClass
{
    public event EventHandler SomeEvent;
}

private class Foo
{
    public Foo()
    {
        SomeStaticClass.SomeEvent += MyHandler;
    }

    private void MyHandler( object sender, EventArgs ) { /* whatever */ }
}

我使用靜態類來使問題在這里盡可能明顯。 讓我們說,在應用程序的生命周期中,創建了許多Foo對象。 每個Foo訂閱靜態類的SomeEvent事件。

Foo對象可能會在某個時間或某個時間超出范圍,但靜態類通過事件處理程序委托維護對每個對象的引用。 因此,它們無限期地保持活着。 在這種情況下,事件處理程序只需要“取消掛鈎”。

... XNA項目似乎已經保存了很多我確實使用的字節數組...

你可能在LOH中遇到了碎片。 如果您經常分配大型對象,則可能會導致問題。 這些對象的總大小可能比分配給運行時的總內存小得多,但由於碎片,會為應用程序分配大量未使用的內存。

我上面鏈接的探查器會告訴你這是否有問題。 如果是,您可能會將其追蹤到某處的物體泄漏。 我剛剛在我的應用程序中修復了一個顯示相同行為的問題,這是因為即使在調用Dispose()之后, MemoryStream沒有釋放其內部byte[] 將流包裝在虛擬流中並將其清零以解決問題。

另外,說明顯而易見的,確保Dispose()實現IDisposable的對象。 可能存在本地資源。 再次,一個好的剖析器將抓住這一點。

我的建議; 它不是GC,問題出在你的應用程序中。 使用分析器,讓您的應用程序處於高內存消耗狀態,獲取內存快照並開始分析。

首先,GC工作正常,效果很好。 你剛剛發現它沒有錯誤。

現在我們已經解決了這個問題,一些想法:

  1. 你使用太多線程嗎?
  2. 請記住,GC是非確定性的; 只要它認為需要運行它就會運行(即使你調用GC.Collect()
  3. 您確定所有參考文獻都超出了范圍嗎?
  4. 你首先在內存中加載了什么? 大圖像? 大文本文件?

你的探查器應該告訴你什么使用了這么多的內存。 盡可能多地開始削減最大的罪魁禍首。

此外,每隔X秒調用GC.Collect()是個壞主意,不太可能解決您的實際問題。

分析.NET中的內存問題並不是一項簡單的任務,您肯定應該閱讀幾篇好文章並嘗試使用不同的工具來實現結果。 調查結束后,我最終得到了以下文章: http//www.alexatnet.com/content/net-memory-management-and-garbage-collector您也可以嘗試閱讀一些Jeffrey Richter的文章,如下文: httphttp ://msdn.microsoft.com/en-us/magazine/bb985010.aspx

根據我的經驗,內存不足問題有兩個最常見的原因:

  1. 事件處理程序 - 即使沒有其他對象引用它,它們也可以保存對象。 理想情況下,您需要取消訂閱事件處理程序以自動銷毀對象。
  2. 終結器線程在STA模式下被某些其他線程阻止。 例如,當STA線程執行大量工作時,其他線程將停止,並且無法銷毀終結隊列中的對象。

編輯:添加了大對象堆碎片的鏈接。

編輯:因為它看起來像分配和丟棄紋理是一個問題,你可以使用Texture2D.SetData重用大字節[] s?

首先,您需要弄清楚它是否是泄漏的托管或非托管內存。

  1. 使用perfmon查看進程中的.net內存#Bytes'和Process\\Private Bytes 比較數字和內存上升。 如果Private字節的增長超過堆內存的增長,那么它就是非托管內存增長。

  2. 非托管內存增長將指向未被處理的對象(但最終會在其終結器執行時收集)。

  3. 如果它是托管內存增長,那么我們需要查看哪一代/ LOH(每一代堆字節也有性能計數器)。

  4. 如果它是大對象堆字節,您將需要重新考慮使用和丟棄大字節數組。 也許字節數組可以重復使用而不是丟棄。 另外,考慮分配2的冪的大字節數組。這樣,當處理時,你將在大對象堆中留下一個大的“洞”,可以被另一個相同大小的對象填充。

  5. 最后一個問題是寄托記憶,但我對此沒有任何建議,因為我從來沒有搞過它。

我還要補充一點,如果您正在進行任何文件訪問,請確保您正在關閉和/或處置任何讀者或作者。 打開任何文件並關閉它之間應該匹配1-1。

另外,我通常使用using子句來獲取資源,比如Sql Connection:

using (var connection = new SqlConnection())
{
  // Do sql connection work in here.
}

您是否在任何對象上實現IDisposable並且可能執行導致任何問題的自定義操作? 我會仔細檢查你所有的IDisposable代碼。

GC沒有考慮非托管堆。 如果您正在創建大量僅僅是C#中的包裝器來處理更大的非托管內存,那么您的內存將被吞噬,但GC無法基於此做出合理的決策,因為它只能看到托管堆。

你最終會遇到GC收集器認為你沒有內存不足的情況,因為你的gen 1堆上的大多數東西都是8字節的引用,實際上它們就像海上的冰山一樣。 大部分內存都在下面!

您可以使用這些GC調用:

System::GC::AddMemoryPressure(sizeOfField);
System::GC::RemoveMemoryPressure(sizeOfField);

這些方法允許垃圾收集器查看非托管內存(如果您提供正確的數字)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM