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

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

      ——從內存泄露說起

提要

  內存泄露發生了怎麼辦?如何預防內存泄露的發生?我的經驗是解決內存泄露的根本辦法是編碼時有預防意識。

  目錄

1.內存泄露

  1.1怎樣纔算是發生了內存泄露

  1.2判斷工具(perfmon.msc)

    1.2.1perfmon.msc的使用

     1.2.2一些重要的性能計數器

   1.3其他檢測工具

2.如何在日常編程中預防內存泄露的發生

  2.1 Dispose()的使用

  2.2 using的使用

  2.3 事件的卸載

  2.4繼承 IDisposable

    Net如何繼承IDisposable接口,實現自己的Dispose()函數

4.其他資源泄露

   GDI leak,handle leak。

 5.幾個特例

 6.參考文獻

  (1)發現並防止託管代碼中出現內存泄漏 ;

 (2)

正文

1.內存泄露

   剛開始使用Net的讀者(甚至做了一兩年商業開發的同行)可能對Net的內存泄露不是很瞭解,甚至會說Net不存在內存泄露,他們會問“不是有GC機制嗎?”恩,是有這麼回事,它保證了通常應用時不用考慮頭疼的資源釋放問題,但這個機制不保證你開發的程序就不存在內存泄露(在此我們假設程序不存在邏輯錯誤等Bug,下同)。

  同時, 一方面,GC機制本身的缺陷造成的,它做Collect操作時是基於一定的算法,雖然這個算法隨着Net版本的升級在逐步的優化,但還是不能保證及時迅速準確地把Garbage回收(一定程度上也沒必要,特別是隨着機器硬件性能的大幅提升,犯不着這麼做,怎麼着GC也是需要開支的),這就需要在編碼過程中通知那些資源可以被釋放,特別是大對象,否則可能造成“泄露”;另一方面,Net中託管資源和非託管資源的處理是有差異的,託管資源的處理是由GC自動執行的,而非託管資源 (佔少部分,比如文件操作,網絡連接等)必須顯式地釋放,否則就可能造成泄露。綜合起來說的話,由於託管資源在Net中佔大多數,通常不做顯式的資源釋放是可以的,不會造成明顯的資源泄露,而非託管資源則不然,是發生問題的主戰場,是最需要注意的地方。

   另外,照我看來,很多情況下,衰老測試關注的主要是有沒有內存泄露的發生,而對其他泄露的重視次之。爲什麼這樣做呢,我認爲有兩方面的原因,一是內存跟其他資源是正相關的,也就是說沒有內存泄露的發生,其他泄露的發生概率也較小,其根本在於所有的資源最後會反應在內存上;另一個就是很多Net應用開發,用到的非託管資源,多提供Dispose方法的,而Dispose之後,不光內存及時釋放,其他的也做了釋放。因此,通常情況下我們主要關注的是內存的使用情況,而對自定義控件開發等情況則需要關注GDI,handle等其他資源的情況。

1.1怎樣纔算是發生了內存泄露

  上面說了這麼多,那麼到底如何判斷有沒有內存泄露的發生呢?

  如果程序報“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也是我比較關注的。

  通關觀察,你如果發現這些參數是在一個區間內震盪的,應該是沒有大的問題的,如果是一個持續上漲的狀態,那你就得注意,很可能存在內存泄露。


1.2判斷工具(perfmon.msc)

  如何測定以上的計數器呢,我大多使用windows自帶的perfmon.msc。在此稍微說說改工具的使用。

    1.2.1perfmon.msc的使用

 

      由於現在不能貼圖,以後補齊吧。在Run中輸入perfmon.msc,運行,其他的自己摸索,不難。

     1.2.2一些重要的性能計數器

      參考1,

     參考2

1.3其他檢測工具

  我用過的裏面CLRProfiler 和dotTrace 還行(下載地址見鏈接)windeg也還行。不過坦白的說,準確定位比較費勁,最好還是按常規的該Dispose的加Dispose,也可以加GC.Collect()。

2.如何在日常編程中預防內存泄露的發生

  2.1 Dispose()的使用

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

  2.2 using的使用

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

  2.3 事件的卸載

  2.4 API的調用

待續........

 

本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/yuanhuiqiao/archive/2010/01/28/5264480.aspx

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章