2011-02-16 203 views
26

我想了解內存的概念泄漏更好的任何人都可以點一些有用的信息,這將有助於我更好地理解什麼內存泄漏是,我如何才能找到他們在我的代碼。內存泄漏C#

謝謝

+0

Windows註冊表→搜索→「HeapLeakDetection」 – Bitterblue 2013-11-08 14:42:55

+0

這是我剛剛在一篇關於.Net內存泄漏的博客文章:http://crazorsharp.blogspot.com/2009/03/net-memory-leaks-it-is -possible.html – BFree 2011-02-16 18:59:43

回答

3

當你的程序動態地分配不你使用它完成後得到正確釋放的內存時發生內存泄漏。如果你有一個程序持續這樣做,你的泄漏會變得越來越大,很快你的程序佔用你所有的RAM。

10

一個非常良好的閱讀是Everybody thinks about garbage collection the wrong way

通常,內存泄漏,或任何資源泄漏,是每當程序分配內存(或任何其它資源),然後省略當完成它釋放它。在本機應用程序中,內存泄漏是最常見的資源泄漏,當資源引用(指向已分配塊的指針)超出作用域並被銷燬時,可能會發生內存泄漏,但分配的資源(內存塊)不會被破壞。在這種情況下,資源(內存)被泄漏,因爲程序已經失去了釋放它的能力,即使它想要,因爲它不再記住資源的位置(塊的地址)。

在管理應用程序的內存泄漏是有點棘手。由於運行時可以自動跟蹤對資源的引用,因此它還可以理解資源(對象)何時不再被應用程序的任何活動部分引用(在任何線程中,沒有從堆棧幀引用該資源的鏈)因此運行時可以理解何時可以安全地收集應用程序不再引用的對象。因此,在被管理的世界中,如果您認爲應用程序不再引用對象(因此它可以由運行時收集),但是實際上通過某些引用鏈,您可以在時引用「泄漏」它因此無法收集。

我強烈建議Raymond Chen的文章上面鏈接,是非常非常照明。

22

傳統的內存泄漏,當您分配內存,然後以某種方式「忘記」釋放它發生。在舊的C++代碼中,這意味着調用new而沒有相應的delete。在C中,這意味着撥打alloc()/malloc()而沒有相應的free()

在.Net中,您不會因傳統意義上的內存泄漏,因爲您不應該自行釋放內存。相反,你依靠垃圾收集器爲你釋放它。但是,這並不意味着你永遠不會忘記記憶。有幾種方法可能會意外地保留一個引用,以防止垃圾收集器完成它的工作。這些包括全局變量(特別是列表,字典和可能用於「緩存」對象的其他對象),掛在內存上的事件處理程序,遞歸歷史記錄引用以及大對象堆。

在這裏還要注意,很重要的一點是,僅僅因爲您在.Net中看到增加的內存使用模式,它並不一定意味着您的應用程序正在泄漏內存。在整體內存壓力較低的情況下,垃圾收集器可能會選擇通過不收集,或者通過收集但不將內存返回給操作系統來節省時間。

36

存在多種內存泄漏,但通常這個術語指的是某種不再使用的資源,但仍佔用內存。如果你的應用程序中有許多需要大量內存,並最終耗盡了它。

在C#,這些都是一些常見內存泄漏:

  • 不刪除事件偵聽器。任何使用引用外部對象的匿名方法或lambda表達式創建的事件偵聽器都會使這些對象保持活動狀態。請記住在事件監聽器不再使用時刪除事件監聽器。
  • 保持數據庫連接或結果集未使用時處於打開狀態。請記住致電Dispose()所有IDisposable對象。 Use the using statement
  • 使用p/Invoke調用C函數,分配內存然後永遠不會釋放。
4

應將「內存泄漏」定義爲「當您認爲不應該使用的內存」時,將其作爲C#/ Java應用於垃圾收集語言/運行時。

傳統上,「內存泄漏」被定義爲未正確釋放的內存(請參閱其他答案中的維基百科鏈接),這對於垃圾收集環境通常不會發生。請注意,由於運行時問題,即使垃圾收集語言可能會泄漏內存 - 即使JavaScript是垃圾收集語言,也很容易在Internet Explorer的JavaScript運行時中泄漏大量JavaScript對象。

5

將內存分配給應用程序時,應用程序有義務將該內存釋放回操作系統,以便其他應用程序可以重新使用該內存。當應用程序不釋放內存時會發生內存泄漏,從而阻止重新分配內存。

對於託管代碼,垃圾回收器跟蹤對由應用程序創建的對象的引用。對於大多數情況,CLR將以代理正在運行的進程的合理方式透明地處理內存分配和釋放。然而,.NET開發人員仍然需要考慮資源管理,因爲即使垃圾收集器工作仍然存在內存可能泄漏的情況。

考慮下面的代碼:

Widget widget = new Widget();

上面的代碼行創建Widget類的新實例和小部件字段被分配給該對象的引用。 GC跟蹤與每個對象相關的引用,並取消分配沒有強引用的對象的內存。

值得一提的是,CLR的垃圾收集只會收集託管對象,.NET代碼可以並經常使用不能自動垃圾收集的非託管資源。

當分配了這些資源的對象無法在對這些資源的最後一次引用超出範圍之前正確地釋放它們時,會發生非託管資源泄漏,這會導致分配資源,但未重新引用並因此對應用程序不可用。

直接引用非託管資源的類應確保正確釋放這些資源。這樣的例子會是這個樣子:

public void ManagedObject : IDisposable 
{ 
    //A handle to some native resource. 
    int* handle; 

    public ManagedObject() 
    { 
     //AllocateHandle is a native method called via P/Invoke. 
     handle = AllocateHandle(); 
    } 

    public void Dispose() 
    { 
     Dispose(true); 
     GC.SuppressFinalize(this); 
    } 

    private void Dispose(bool disposing) 
    { 
     if (disposing) 
     { 
      //deal with managed resources here 
      FreeHandle(handle); 
     } 
    } 

    ~ManagedType() 
    { 
     Dispose(false); 
    } 
} 

disposing參數是假的終結被調用時。這是爲了防止在終結器中使用管理資源,因爲在該階段管理引用應被視爲無效。

還要注意,Dispose()方法調用GC.SuppressFinalize(this),這會阻止終結器在該實例中運行。這樣做是因爲在終止器中釋放的資源在Dispose調用中被釋放,從而不需要fializer調用。

,使得使用的是處理非託管資源(或者說實現了IDisposable的任何類)類應在 using塊內這樣做,以確保當不再需要訪問資源的 IDisposable.Dispose稱爲這將需要

客戶端代碼關心託管和非託管資源,並且在上面的例子中,確保不會對終結器進行非常昂貴的調用。

散步的appoligies。我現在就停下來。