.NET資源泄露與處理方案知識點分享
.NET雖然擁有強大易用的垃圾回收機制,但并不是因為這樣,你就可以對資源管理放任不管,其實在稍不注意的時候,可能就造成了資源泄露,甚至因此導致系統(tǒng)崩潰,到那時再來排查問題就已經是困難重重。
一、知識點簡單介紹
常見的資源泄露有:
- 內存泄漏:非托管資源沒有釋放、非靜態(tài)對象注冊了靜態(tài)實例。
- GDI泄露:字體。
- 句柄泄露:Socket或線程。
- 用戶對象泄露:移除的對象未釋放。
二、具體實例
1. 內存泄漏
很常見的現象是分不清哪些對象需要釋放,對于控件、Stream等一些非托管資源也只管新增,卻沒有釋放,功能是實現了,卻埋了顆不小的雷。
private void button1_Click(object sender, EventArgs e) { for(int i=0;i<1000;i++) this.Controls.Add(new TabPage()); }
private void button1_Click(object sender, EventArgs e) { new Form2.ShowDialog(); }
如果你覺得寫這樣的代碼很Cool,很簡潔,你在項目中也有這么寫代碼,那你就碰到大麻煩了,你試試在上面Form2中開個大一點的數組來檢查內存,然后運行,按幾下按鈕,你就會發(fā)現,內存一直增加,即使你調用了GC也無濟于事。所以,對于此類非托管資源要記住釋放,用完即廢可以采用using關鍵字。
public Form2() { InitializeComponent(); MyApp.FormChanged += FormChanged; }
上面這個例子中,MyApp是一個靜態(tài)類,如果在實例對象中向這種類里面注冊了事件,而又沒有取消注冊,這樣也會遇到大麻煩,即使在外部已經記得調用了Form2的Dispose也是沒用的。
解決方案
- 注意托管資源和非托管資源的釋放區(qū)別,非托管資源是需要手動釋放的。
- 使用using關鍵字,避免忘記Dispose的情況,如上面的ShowDialog問題。(using中還起到了try-catch的作用,避免由于異常未調用Dispose的情況)
- 使用UnLoad事件或者析構函數,對注冊的全局事件進行取消注冊。
- 特別注意自定義組件的穩(wěn)定性更重要,發(fā)生問題時影響也更廣。注意繼承IDisposable接口,進行資源釋放
2. GDI泄露
一般會跟字體相關,例如我曾在Android上用Cocos2d做一個小游戲時頻繁地切換字體、Dev控件的Font屬性賦值也會有這種現象。
XXX.Font = new Font(...)
解決方案
這個問題我目前是采用字體池來解決,類似線程池的概念,相同Key值取同一個對象。若有更好方案歡迎留言討論
3. 句柄泄露
一般跟Socket和Thread(線程)有關
for(int i=0;i<1000;i++){ new Thread(()=>{ Thread.Sleep(1000); }).Start(); }
解決方案
- Socket的場景暫時沒遇到。
- 線程問題采用線程池相關的輔助類能有效解決,例如ThreadPool、Task、Parallel。
4. 用戶對象泄露
一般跟移除的對象未釋放有關
private void button1_Click(object sender, EventArgs e) { tab.Remove(tabPage); }
三、最后特別奉送一個內存釋放的大招
[DllImport("kernel32.dll", EntryPoint = "SetProcessWorkingSetSize")] public static extern int SetProcessWorkingSetSize(IntPtr process, int minSize, int maxSize); /// <summary> /// 釋放內存 /// </summary> public static void ClearMemory() { GC.Collect(); GC.WaitForPendingFinalizers(); if (Environment.OSVersion.Platform == PlatformID.Win32NT) { SetProcessWorkingSetSize(System.Diagnostics.Process.GetCurrentProcess().Handle, -1, -1); } }
調用以上API能讓你的內存一下爆減,是不是很給力,一調用內存就降下來了。But,先別高興太早,這其實是偽釋放,只是暫時解決內存大量泄漏導致系統(tǒng)崩潰的應急處理方案。具體原因參考:SetProcessWorkingSetSize函數的騙局,關鍵信息:物理內存轉虛擬內存,涉及磁盤讀寫。好處壞處都貼出來了,是否需要使用請君自己斟酌。
四、總結
實際上由于各個開發(fā)人員的水平跟接觸面不同,又沒有經過統(tǒng)一的培訓(各個人對資源釋放的理解與關注度不同,或者寫代碼時就沒考慮內存未被釋放這種問題),發(fā)現問題的時候項目往往已經做到了一個階段,系統(tǒng)也比較龐大了,這種時候才發(fā)現內存泄露的問題確實是很頭疼的。
資源泄露的場景往往是相互關聯的,發(fā)生最多的就是內存泄漏,而除了寫法可能有問題外,也可能是因為句柄泄露或用戶對象泄露引起的。
參考文章:
以上就是本次介紹的全部相關知識點,感謝大家的學習和對腳本之家的支持。
相關文章
asp.net core 獲取 MacAddress 地址方法示例
這篇文章主要介紹了asp.net core獲取MacAddress地址方法,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-02-02ASP.NET Core 應用程序中的靜態(tài)文件中間件的實現
這篇文章主要介紹了ASP.NET Core 應用程序中的靜態(tài)文件中間件的實現,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-04-04