溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

.NET資源泄露如何解決

發(fā)布時(shí)間:2021-06-12 18:36:58 來源:億速云 閱讀:134 作者:Leah 欄目:開發(fā)技術(shù)

.NET資源泄露如何解決,針對這個(gè)問題,這篇文章詳細(xì)介紹了相對應(yīng)的分析和解答,希望可以幫助更多想解決這個(gè)問題的小伙伴找到更簡單易行的方法。

一、知識點(diǎn)簡單介紹

常見的資源泄露有:

  • 內(nèi)存泄漏:非托管資源沒有釋放、非靜態(tài)對象注冊了靜態(tài)實(shí)例。

  • GDI泄露:字體。

  • 句柄泄露:Socket或線程。

  • 用戶對象泄露:移除的對象未釋放。

二、具體實(shí)例

1. 內(nèi)存泄漏

很常見的現(xiàn)象是分不清哪些對象需要釋放,對于控件、Stream等一些非托管資源也只管新增,卻沒有釋放,功能是實(shí)現(xiàn)了,卻埋了顆不小的雷。

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,很簡潔,你在項(xiàng)目中也有這么寫代碼,那你就碰到大麻煩了,你試試在上面Form2中開個(gè)大一點(diǎn)的數(shù)組來檢查內(nèi)存,然后運(yùn)行,按幾下按鈕,你就會發(fā)現(xiàn),內(nèi)存一直增加,即使你調(diào)用了GC也無濟(jì)于事。所以,對于此類非托管資源要記住釋放,用完即廢可以采用using關(guān)鍵字。

public Form2()
{
 InitializeComponent();
 MyApp.FormChanged += FormChanged;
}

上面這個(gè)例子中,MyApp是一個(gè)靜態(tài)類,如果在實(shí)例對象中向這種類里面注冊了事件,而又沒有取消注冊,這樣也會遇到大麻煩,即使在外部已經(jīng)記得調(diào)用了Form2的Dispose也是沒用的。

解決方案

  • 注意托管資源和非托管資源的釋放區(qū)別,非托管資源是需要手動(dòng)釋放的。

  • 使用using關(guān)鍵字,避免忘記Dispose的情況,如上面的ShowDialog問題。(using中還起到了try-catch的作用,避免由于異常未調(diào)用Dispose的情況)

  • 使用UnLoad事件或者析構(gòu)函數(shù),對注冊的全局事件進(jìn)行取消注冊。

  • 特別注意自定義組件的穩(wěn)定性更重要,發(fā)生問題時(shí)影響也更廣。注意繼承IDisposable接口,進(jìn)行資源釋放

2. GDI泄露

一般會跟字體相關(guān),例如我曾在Android上用Cocos2d做一個(gè)小游戲時(shí)頻繁地切換字體、Dev控件的Font屬性賦值也會有這種現(xiàn)象。

XXX.Font = new Font(...)

解決方案

這個(gè)問題我目前是采用字體池來解決,類似線程池的概念,相同Key值取同一個(gè)對象。若有更好方案歡迎留言討論

3. 句柄泄露

一般跟Socket和Thread(線程)有關(guān)

for(int i=0;i<1000;i++){
 new Thread(()=>{
 Thread.Sleep(1000);
 }).Start();
}

解決方案

  • Socket的場景暫時(shí)沒遇到。

  • 線程問題采用線程池相關(guān)的輔助類能有效解決,例如ThreadPool、Task、Parallel。

4. 用戶對象泄露

一般跟移除的對象未釋放有關(guān)

private void button1_Click(object sender, EventArgs e)
{
 tab.Remove(tabPage);
}

三、最后特別奉送一個(gè)內(nèi)存釋放的大招

[DllImport("kernel32.dll", EntryPoint = "SetProcessWorkingSetSize")]
public static extern int SetProcessWorkingSetSize(IntPtr process, int minSize, int maxSize);
/// <summary> 
/// 釋放內(nèi)存 
/// </summary> 
public static void ClearMemory()
{
 GC.Collect();
 GC.WaitForPendingFinalizers();
 if (Environment.OSVersion.Platform == PlatformID.Win32NT)
 {
 SetProcessWorkingSetSize(System.Diagnostics.Process.GetCurrentProcess().Handle, -1, -1);
 }
}

調(diào)用以上API能讓你的內(nèi)存一下爆減,是不是很給力,一調(diào)用內(nèi)存就降下來了。But,先別高興太早,這其實(shí)是偽釋放,只是暫時(shí)解決內(nèi)存大量泄漏導(dǎo)致系統(tǒng)崩潰的應(yīng)急處理方案。具體原因參考:SetProcessWorkingSetSize函數(shù)的騙局,關(guān)鍵信息:物理內(nèi)存轉(zhuǎn)虛擬內(nèi)存,涉及磁盤讀寫。好處壞處都貼出來了,是否需要使用請君自己斟酌。

四、總結(jié)

實(shí)際上由于各個(gè)開發(fā)人員的水平跟接觸面不同,又沒有經(jīng)過統(tǒng)一的培訓(xùn)(各個(gè)人對資源釋放的理解與關(guān)注度不同,或者寫代碼時(shí)就沒考慮內(nèi)存未被釋放這種問題),發(fā)現(xiàn)問題的時(shí)候項(xiàng)目往往已經(jīng)做到了一個(gè)階段,系統(tǒng)也比較龐大了,這種時(shí)候才發(fā)現(xiàn)內(nèi)存泄露的問題確實(shí)是很頭疼的。

資源泄露的場景往往是相互關(guān)聯(lián)的,發(fā)生最多的就是內(nèi)存泄漏,而除了寫法可能有問題外,也可能是因?yàn)榫浔孤痘蛴脩魧ο笮孤兑鸬摹?/p>

關(guān)于.NET資源泄露如何解決問題的解答就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關(guān)注億速云行業(yè)資訊頻道了解更多相關(guān)知識。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI