文章目錄
按順序第二篇應該是”如何檢測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.第三方架構引來的代碼複雜性應該被考慮。