熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> .NET編程 >> 正文

未釋放事件Handler可能導致內存洩漏

2013-11-13 09:51:02  來源: .NET編程 

  以前曾看見過這樣一個問題托管代碼會不會導致內存洩漏自己對GC的了解也不是很深但還是比較贊成這樣的觀點托管代碼不會產生內存洩漏除非你沒有正確釋放非托管資源

  今天看到一個非常有趣的例子關於沒有釋放事件的Handler導致的內存洩漏

  以前對於釋放Handler的觀念是一點也沒有這主要因為沒此方面的意識沒有養成好的習慣只知道當關心這個事件的時候就注冊一下 暫時不關心了就移除掉卻從來沒有想到最終不移除不必要的Handler會導致此類無法被正常回收導致不必要的內存浪費

  事情是這樣的今天在看項目Source Code的時候發現一個有趣的字眼WeakEvent 自己以前對WeakReference有點了解所以就好奇地看看這是個啥玩意

  發現其是一種通過弱引用實現的Delegate因為沒有太多的注釋所有不知其為啥用此種方式來封裝事件於是順手Google了一下找到了一篇關於weak event的非常有意思的文章

  文章裡提出了一個問題場景如下

  UnRelease Event Handler

  using System

  using SystemCollectionsGeneric

  using SystemText

  using MicrosoftWin

  namespace ConsoleApplication

  {

  class DisplaySettingsListener

  {

  byte[] m_ExtraMemory = new byte[]

  public DisplaySettingsListener()

  {

  SystemEventsDisplaySettingsChanged += new EventHandler(ehDisplaySettingsChanged)

  }

  private void ehDisplaySettingsChanged(object sender EventArgs e)

  {

  }

  }

  class Program

  {

  static void DisplayMemory()

  {

  ConsoleWriteLine(Total memory {###########} bytes GCGetTotalMemory(true))

  }

  static void Main()

  {

  DisplayMemory()

  ConsoleWriteLine()

  for (int i = i < i++)

  {

  ConsoleWriteLine(—— New Listener #{} —— i +

  DisplaySettingsListener listener = new DisplaySettingsListener()

  listener = null

  GCCollect()

  DisplayMemory()

  }

  ConsoleRead()

  }

  }

  }

  運行的結果如下

   

  雖然我們釋放了對listener的引用並且強制GC進行回收但我們可以看到其內存占用量還是變大了出乎了我的意料

  這就是該文作者指出的事件列表裡保存的是一個強命名的引用而非弱引用雖然上面釋放了listener變量對Listener實例的引用但因為仍然在DisplaySettingsChanged事件列表裡保存了對Listener實例的引用導致Listener實例並不能被垃圾回收(有人引用自然不會回收)

  那麼接下來看看下面的代碼

  Release Event Hanlder

  class DisplaySettingsListener IDisposable

  {

  byte[] m_ExtraMemory = new byte[]

  public DisplaySettingsListener()

  {

  SystemEventsDisplaySettingsChanged += new EventHandler(ehDisplaySettingsChanged)

  }

  private void ehDisplaySettingsChanged(object sender EventArgs e)

  {

  }

  IDisposable Members#region IDisposable Members

  public void Dispose()

  {

  SystemEventsDisplaySettingsChanged = new EventHandler(ehDisplaySettingsChanged)

  }

  #endregion

  }

  class Program

  {

  static void DisplayMemory()

  {

  ConsoleWriteLine(Total memory {###########} bytes GCGetTotalMemory(true))

  }

  static void Main()

  {

  DisplayMemory()

  ConsoleWriteLine()

  for (int i = i < i++)

  {

  ConsoleWriteLine(—— New Listener #{} —— i +

  DisplaySettingsListener listener = new DisplaySettingsListener()

  listenerDispose()

  listener = null

  GCCollect()

  DisplayMemory()

  }

  ConsoleRead()

  }

  }

  運行結果如下

   

  結果是不是正如您猜測的呢已經成功地回收了listener實例 不知為何從字節變到字節哪位高手賜教一下啊

  詳情可以看原文 The Problem With Delegates

  在後續的文章中作者類似文章開頭提到的Weak Event來解決這個問題 Solving the Problem with Events Weak Event Handlers

  也許您覺得寫這樣的一個Weak Event沒有必要或者顯得麻煩但您一定要記得及時地在必要的地方調用 = 取消不再關心的事件本文的目的也只是在此方面提個善意的提醒


From:http://tw.wingwit.com/Article/program/net/201311/11737.html
    推薦文章
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.