2009-09-09 23 views
2

假設我們有一個名爲SimpleButton1的單一Windows窗體。以下代碼導致無法控制的內存使用量。我做錯了什麼?.NET事件導致無法控制的內存使用情況

我的理解是,在for循環每次迭代,GC清理任何TestClass的對象,因爲有對任何事件沒有處理程序會照顧任何關聯的事件,以及

Public Class Form1 

Private Sub SimpleButton1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles SimpleButton1.Click 
    For i = 1 To 1000000 
     Dim test1 As New TestClass    
    Next 
End Sub 
End Class 

Public Class TestClass 
    Private Event TestEvent(ByVal sender As Object) 
End Class 

請注意,我已嘗試實現IDisposable並在For循環的每次迭代結束時調用test1.Dispose(),但我懷疑我沒有處理正確的資源。

*解答:代碼沒有問題,並且按預期運行。問題是我在調試模式下運行它,並且創建的開銷導致大量內存使用。見下面的討論。

+2

您是否遇到此_Exact_代碼的內存問題?因爲我無法讓它在TaskManagers內存圖中形成一點點凹凸。 –

+0

是的 - 這個確切的代碼。沒有其他處理程序訂閱TestEvent或其他任何創建TestClass實例的處理程序。只需一個簡單的按鈕,即可實現Dim TestClass一百萬次。 – stacked

+2

嗯 - 不可重現。請重新測試。 –

回答

3

如果您正在調試下運行,VisualStudio中會產生額外的代碼,以幫助編輯和繼續(_EncList的東西)即使你不在調試器下。

請務必編譯並編譯爲「發行版」。是的,這使得當它們的行爲不同時很難調試內存問題,但這並不是追蹤內存問題最難的事情。

5

你沒有在這裏展示什麼,我懷疑問題是在你的TestClass中,你有事件TestEvent,而你可能會向TestEvent添加處理程序,你可能不會刪除它們。通過不刪除它們,你告訴GC不要收集TestClass或處理程序的類。

+1

回覆:詹姆斯 我沒有添加任何處理程序TestEvent,除了在編譯或運行時添加可能的處理程序。除了用這種方式創建的默認處理程序之外,沒有什麼我希望需要刪除的。 .NET代碼中還有其他的東西,我可以告訴你具體到你的評論,還是有什麼辦法來訪問處理程序添加在運行時,如果這實際上是問題? – stacked

+2

James,TestEvent的處理程序是TestClass的__outgoing__引用,並且不會阻止GC。除非實例互相訂閱。 –

3
  1. GC在每次迭代中都不會啓動,它會一直等待到適當的時候並且集體掃描這些對象。

  2. TestEvent不應該阻止GC的收集,但是如果TestClass的實例訂閱了另一個對象上的事件,那麼這將使它們保持活動狀態。沒有自動清理。

  3. 如何確定「導致無法控制的內存使用量」。測量內存使用情況比聽起來難。我希望這不是基於TaskManager。

+0

感謝您的評論。關於點3: 它基於任務管理器。我寫過的這個簡單的應用程序是我在一個更大的應用程序中挑出問題的結果。內存使用量的增加最終會導致內存不足異常。 – stacked

+2

@ Stacked-在較大的一箇中,您可能有其他人已經描述的一些問題,特別是關於事件。 – RichardOD

+0

RichardOD:如果我將「Private Event」行註釋掉 - 沒有內存使用問題。這是同樣發生的更大的應用程序。我的理解是我的事件沒有處理程序,因爲如果我註釋掉事件聲明,除了內存使用情況外,它不會以任何方式影響應用程序。我理解錯誤嗎? – stacked

5

這段代碼,正如你所示,它不會泄漏。垃圾回收器最終會照顧你創建的類。然而,如果你有一個Observable類和一個Observer類,Observer將會調用Observable.Event += Observer.EventHandler;通過這樣做,Observable現在有一個引用返回到Observer。如果你不打電話Observable.Event -= Observer.EventHandler,那麼這個參考文件仍然存在。

如果您認爲沒有人提及Observer,但Observable具有較長的使用期限,則會發生此問題。雖然Observable有一個Observer的引用,但除非清除所有處理程序(this.Event = null),否則沒有任何代碼可以刪除引用。

這實際上是一個內存泄漏!

有幾種方法來解決這個問題:

  1. 呼叫 - =你扔出去的觀察員之前。有可能在的Dispose()
  2. 使用Weak Events
  3. 考慮一個不同的事件觸發模式,像Event Aggregator
+0

謝謝Brian。你說「垃圾收集器最終會照顧你創建的類」 - 何時「最終」?如果我編譯這個程序並運行它,任務管理器中的Mem用法將失去控制,並且永遠不會被回收。這最終導致內存不足異常。在我看來,GC不會照顧我創建的任何類。 – stacked