Silverlight記憶體泄露(二)解決記憶體泄露之Dispose誤用

來源:互聯網
上載者:User
文章目錄
  •  
 

按順序第二篇應該是”如何檢測SIlverlight記憶體泄露”,但這一系列都是解決實際問題的日誌,有些檢測結果沒有被儲存下來,不可能為了寫這些文章,而重現bug。想到哪就寫到哪了。

看到許多文章在解決事件為被登出引起記憶體泄露時,通過在Dispose中登出事件,以釋放資源,這可能會出現未預料的問題。

Dispose模式介紹

參考:Effective C# - 條款18:實現標準的Dispose模式

要點:

通過實現IDisposable介面,你寫成了兩件事:第一就是提供了一個機制來及時的釋放所有佔用的託管資源,另一個就是你提供了一個標準的模式讓使用者來釋放非託管資源。這是十分重要的,當你在你的類型上實現了IDisposable介面以後,使用者就可以避免析構時的損失。你的類就成了.Net社 區中表現相當良好的成員。

但在你建立的機制中還是存在一些漏洞。如何讓一個衍生類別清理自己的資源,同時還可以讓基類很好的再做資源清理呢?(譯註:因為調用Dispose方法時, 必須調用基類的Dispose,當然是在基類有這個方法時。但前面說過,我們只有一個標記來標識對象是否處理過,不管先調用那個,總得有一個方法不能處理這個標記,而這就存在隱患) 如果基類重載了解構函式,或者自己添加實現了IDisposable介面,而這些方法又都是必須調用基類的方法的;否則,基類無法恰當的釋放資源。同樣,析構和處理共用了一些相同的職責:幾乎可以肯定你是複製了析構方法和處理方法之間的代碼。正如你會在原則26中學到的,重載介面的方法根本沒有如你所期望的那樣工作。Dispose標準模式中的第三個方法,通過一個受保護的輔助性虛函數,製造出它們的常規任務並且掛接到衍生類別來釋放資源。基類包含介面的核心代碼, 衍生類別提供的Dispose()虛函數或者解構函式來負責清理資源

Dispose可能存在的問題

Dispose是Object就有的方法,任何代碼都有可能調用,並且子類應該調用基類的Dispose。從中可看出:

1. Dispose是.Net固有的方法,任何代碼都可能調用

2. Dispose是自上而下執行的,基類到子類。

這表明Dispose是不可控的,其他代碼很可能會調用這個方法。

MVVM light最初也依賴Dispose釋放資源,但在新版本中已經使用ICleanUp介面釋放資源,就是由於Dispose太不可控了。但為了相容前版本,Dispose仍然有效。

Dispose引起問題的例子例子[ViewModelExport(typeof(ChapterViewModel), "Chapter")]

[PartCreationPolicy(CreationPolicy.NonShared)]

public class ChapterViewModel:NavViewModel

{

ChapterService service = new ChapterService();

public C

[ViewModelExport(typeof(ChapterViewModel), "Chapter")]

[PartCreationPolicy(CreationPolicy.NonShared)]

public class ChapterViewModel:NavViewModel

{

ChapterService service = new ChapterService();

public ChapterViewModel()

{

service.Loaded += new EventHandler<AsyncEventArgs<object>>(service_Loaded);

}

void service_Loaded(object sender, AsyncEventArgs<object> arg)

{

}

public void Dispose()

{

service.Loaded -= new EventHandler<AsyncEventArgs<object>>(service_Loaded);

}

}

hapterViewModel()

{

service.Loaded += new EventHandler<AsyncEventArgs<object>>(service_Loaded);

}

void service_Loaded(object sender, AsyncEventArgs<object> arg)

{

}

public void Dispose()

{

service.Loaded -= new EventHandler<AsyncEventArgs<object>>(service_Loaded);

}

}


按照預想Dispose會登出事件,避免記憶體泄露,但運行時會發現service_Loaded可能不被執行,原因是 Dispose()被第三方程式調用了,而這些調用可能很隱蔽。

在這個程式裡,下面的代碼調用了Dispose,並且不易被發現。

var viewModelFactory = viewModelMapping.CreateExport();

viewModel = viewModelFactory.Value as IViewModel;

viewModelFactory.Dispose();
解決方式

通過其他方式登出事件,比如顯式的介面、弱引用、匿名方法,在事件引起的記憶體泄露的文章中會詳述幾種解決方式。

在MVVM Light的cleanup登出事件

public override void Cleanup()

{

service.Loaded -= new EventHandler<AsyncEventArgs<object>>(service_Loaded);

Messenger.Default.Unregister(this);

base.Cleanup();

}
MVVM Light的問題

按上面的方式本應該解決問題,但是service_Loaded 仍有可能不被執行。原來Mvvm Light為了保持和上一版本的相容性在Dispose方法中調用了Cleanup。

重載Dispose

protected override void Dispose(bool disposing)

{

//,覆蓋基類
禁止調用cleanup

}
結論

1. 盡量不要用Dispose釋放託管資源,而使用自己的介面。

2. Dispose中絕對不應該調用其他方法,像MVVM light中調用cleanup,就會導致問題。

3.第三方架構引來的代碼複雜性應該被考慮。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.