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