Net資源洩露(內存洩露,GDI洩露,handle 洩露等)的終極解決方案 

摘要

  本文主要討論了,什麼是.Net內存洩露?如何確定是發生了內存洩露?如何預防內存洩露的發生?

正文
1.dot Net內存洩露簡介

   可能很多.Net的用戶(甚至包括一些dot Net開發者)對Net的內存洩露不是很了解,甚至會說.Net不存在內存洩露,因為“不是有GC機制嗎?”----恩,是有這麼回事,它可以讓你在通常應用中不用考慮令人頭疼的資源釋放問題,但很遺憾的是這個機制不保證你開發的程序就不存在內存洩露。甚至可以說,dot Net中內存洩露是很常見的。這是因為:  一方面,GC機制本身的缺陷造成的;另一方面,Net中託管資源和非託管資源的處理是有差異的,託管資源的處理是由GC自動執行的(執行時機是不可預知的),而非託管資源 (佔少部分,比如文件操作,網絡連接等)必須顯式地釋放,否則就可能造成洩露。綜合起來說的話,由于託管資源在Net中佔大多數,通常不做顯式的資源釋放是可以的,不會造成明顯的資源洩露,而非託管資源則不然,是發生問題的主戰場,是最需要注意的地方。 另外,很多情況下,衰老測試主要關注的是有沒有內存洩露的發生,而對其他洩露的重視次之。這是因為,內存跟其他資源是正相關的,也就是說沒有內存洩露的發生,其他洩露的發生概率也較小,其根本原因在于幾乎所有的資源最後都會在內存上有所反應。
2..Net內存洩露的檢測
有沒有內存洩露的發生?判斷依據是那些?
  如果程序報“Out of memory”之類的錯誤,事實上也佔據了很大部分的內存,應該說是典型的內存洩露,這種情況屬于徹底的Bug,解決之道就是找到問題點,改正。但我的經驗中,這種三下兩下的就明顯的洩露的情況較少,除非有人在很困的情況下編碼,否則大多是隱性或漸進式地洩露,這種需經過較長時間的衰老測試才能發現,或者在特定條件下才出現,對這種情況要確定問題比較費勁,有一些工具(詳見1.3)可以利用,但我總感覺效果一般,也可能是我不會使用吧,我想大型程序估計得無可奈何的用這個,詳細的參見相關手冊。
  需要強調的是,判斷一個程序是不是出現了"memory leak",關鍵不是看它佔用的內存有多大,而是放在一個足夠長的時期(程序進入穩定運行狀態後)內,看內存是不是還是一直往上漲,因此,剛開始的漲動或者前期的漲動不能做為洩露的充分證據。
  以上是些比較感性的說法,實際操作中是通過一些性能計數器來測定的。大多數時候,主要關注 Process 裡的以下幾個指標就能得出結論,如果這些量整體來看是持續上升的,基本可以判斷是有洩露情況存在的。
  A.Handle Count
  B.Thread Count
  C.Private Bytes
  D.Virtual Bytes
  E.Working Set
  F.另外.NET CLR Memory下的Bytes in all heeps也是我比較關注的。
  通過觀察,如果發現這些參數是在一個區間內震蕩的,應該是沒有大的問題,但如果是一個持續上漲的狀態,那就得注意,很可能存在內存洩露。
3.內存洩露診斷工具
    3.1perfmon.msc 的使用

       如何測定那些性能計數器呢,大多使用windows自帶的perfmon.msc。

      在Run中輸入perfmon.msc,運行,其他的自己摸索,不難。
   3.2 其他重要的性能計數器

      其他重要的計數器
   3.3其他檢測工具

  我用過的工具中CLRProfiler 和dotTrace 還行,windeg也還行。不過坦白的說,準確定位比較費勁,最好還是按常規的該Dispose的加Dispose,也可以加 GC.Collect()。
4.如何制造出健壯的程序
  4.1 Dispose()的使用

     如果使用的對象提供Dispose()方法,那麼當你使用完畢或在必要的地方(比如Exception)調用該方法,特別是對非託管對象,一定要加以調 用,以達到防止洩露的目的。另外很多時候程序提供對Dispose()的擴展,比如Form,在這個擴展的Dispose方法中你可以把大對象的引用什麼 的在退出前釋放。

   對于DB連接,COM組件(比如OLE組件)等必須調用其提供的Dispose方法,沒有的話最好自己寫一個。
  4.2 using的使用

   using除了引用Dll的功用外,還可以限制對象的適用範圍,當超出這個界限後對象自動釋放,比如
  4.3 事件的卸載

   這個不是必須的,推薦這樣做。之前注冊了的事件,關閉畫面時應該手動注銷,有利于GC回收資源。
  4.4 API的調用

       一般的使用API了就意味著使用了非託管資源,需要根據情況手動釋放所佔資源,特別是在處理大對象時。
  4.5繼承 IDisposable實現自己內存釋放接口

    Net 如何繼承IDisposable接口,實現自己的Dispose()函數
  4.6弱引用(WeakReference )

     通常情況下,一個實例如果被其他實例引用了,那麼他就不會被GC回收,而弱引用的意思是,如果一個實例沒有被其他實例引用(真實引用),而僅僅是被弱引 用,那麼他就會被GC回收。
  4.7析構函數(Finalize())

     使用了非託管資源的時候,可以自定義析構函數使得對象結束時釋放所佔資源;       

     對僅使用託管資源的對象,應盡可能使用它自身的Dispose方法,一般不推薦自定義析構函數。
5.其他資源洩露

   GDI leak,handle leak。
6. 幾個特例

1.對于使用了Bitmap對象的部分需要調用DestroyIcon來刪除對象

 [DllImport("user32", EntryPoint = "DestroyIcon")]
 private static extern void DestroyIcon(IntPtr handle);


 using (System.IO.MemoryStream mstream = new System.IO.MemoryStream())
 {
     bmp.Save(mstream, System.Drawing.Imaging.ImageFormat.Png);
     intP = new Bitmap(mstream).GetHicon(); this.notifyIcon.Icon = Icon.FromHandle(intP);
     DestroyIcon(intP);
 }  

2. 多線程中的GC         

             //Force garbage collection.
            GC.Collect();
            //Wait for all finalizers to complete
            GC.WaitForPendingFinalizers();
7.參考文獻

  1.發 現並防止 託管代碼中出現內存洩漏 ;

  2..NET Memory Leak reader email: Are you really “leaking” .net memory

  3.How to detect and avoid memory and resources leaks in .NET applications

 4.其他參考資料
8.擴展閱讀

1.實 用.net內存洩露(memory leak)解決方案

2.Garbage Collection: Automatic Memory Management in the Microsoft .NET Framework By Jeffrey Richter  http://msdn.microsoft.com/en-us/magazine/bb985010.aspx

3.OutOfMemoryException and Pinning

https://blogs.msdn.com/yunjin/archive/2004/01/27/63642.aspx

4.http://blogs.msdn.com/b/tess/

5.http://msdn.microsoft.com/zh-cn/library/0xy59wtx%28v=VS.80%29.aspx

後記

   其實本文更像是一篇如何開發出更健壯dot Net程序的指南。


from:http://blog.csdn.net/yuanhuiqiao/archive/2010/01/28/5264480.aspx
arrow
arrow
    全站熱搜

    天才R 發表在 痞客邦 留言(0) 人氣()