Silverlight内存泄露(二)解决内存泄露之Dispose误用

 

按顺序第二篇应该是”如何检测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.第三方框架引来的代码复杂性应该被考虑。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值