[英]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工作正常,效果很好。 你剛剛發現它沒有錯誤。
現在我們已經解決了這個問題,一些想法:
GC.Collect()
。 你的探查器應該告訴你什么使用了這么多的內存。 盡可能多地開始削減最大的罪魁禍首。
此外,每隔X秒調用GC.Collect()是個壞主意,不太可能解決您的實際問題。
分析.NET中的內存問題並不是一項簡單的任務,您肯定應該閱讀幾篇好文章並嘗試使用不同的工具來實現結果。 調查結束后,我最終得到了以下文章: http : //www.alexatnet.com/content/net-memory-management-and-garbage-collector您也可以嘗試閱讀一些Jeffrey Richter的文章,如下文: http : http ://msdn.microsoft.com/en-us/magazine/bb985010.aspx
根據我的經驗,內存不足問題有兩個最常見的原因:
編輯:添加了大對象堆碎片的鏈接。
編輯:因為它看起來像分配和丟棄紋理是一個問題,你可以使用Texture2D.SetData重用大字節[] s?
首先,您需要弄清楚它是否是泄漏的托管或非托管內存。
使用perfmon查看進程中的.net內存#Bytes'和Process\\Private Bytes
。 比較數字和內存上升。 如果Private字節的增長超過堆內存的增長,那么它就是非托管內存增長。
非托管內存增長將指向未被處理的對象(但最終會在其終結器執行時收集)。
如果它是托管內存增長,那么我們需要查看哪一代/ LOH(每一代堆字節也有性能計數器)。
如果它是大對象堆字節,您將需要重新考慮使用和丟棄大字節數組。 也許字節數組可以重復使用而不是丟棄。 另外,考慮分配2的冪的大字節數組。這樣,當處理時,你將在大對象堆中留下一個大的“洞”,可以被另一個相同大小的對象填充。
最后一個問題是寄托記憶,但我對此沒有任何建議,因為我從來沒有搞過它。
我還要補充一點,如果您正在進行任何文件訪問,請確保您正在關閉和/或處置任何讀者或作者。 打開任何文件並關閉它之間應該匹配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.