dinghao

记录成长点滴

 

Silverlight内存泄露(七)Command

 

MVVM中View与ViewModel是强引用的双向关联关系,容易发生内存泄露,任何一方没有被回收都会导致另一方不能被GC回收。

Comand连接了View与ViewModel,容易产生内存泄露。

发现内存泄露

在几个View间导航几次,两次导航到View页面,获取内存快照,发现Info有两个实例。内存没有被释放。

为了每次导航到消息页面,都保存上一次显示的信息,InfoViewModel采用了单例模式,作为缓存,InfoViewModel只有一个实例,按照设想Info也应该只有一个实例。

代码:

var lazyViewModelMapping= LazyViewModelExports.FirstOrDefault(o => o.Metadata.Key.Equals(relativeUri.Host, StringComparison.OrdinalIgnoreCase));

viewModel = lazyViewModelMapping.Value;

var viewFactory = viewMapping.CreateExport();

view = viewFactory.Value as Control;

viewFactory.Dispose();//释放View内存

view.DataContext = viewModel;

是什么原因造成viewFactory.Dispose();没有释放View的内存呢?

clip_image002

确定泄露原因

看下面两张Info图:

正常图:

image

发生泄露图:View是当前页面:

commandLeak

发生泄露图:View不是当前页面:

commandLeak2

可看出stopDownloadCommand在View与ViewModel间建立了强引用关系,遇到ViewModel这个单例模式(生存期与应用程序一样)造成View不能被回收。

解决内存泄露

断开stopDownloadCommand内存得到释放,从上图可看出断开这个command并没有从根本解决问题,增加其他Command仍会产生问题。

clip_image004

根本原因是ViewModel在此不应该使用单例模式,Lazy创建了单例,而应该缓存Modle。

弹出窗口是最好的方式,由于Silverlight4不支持子窗口,在此使用单例作为缓存,没想到造成View不能被释放。

总结

a) 绑定View的类(如VM),使用单例或缓存VM需要谨慎。VM的长生命周期将导致View不能被释放,并且每次导航到View,View的实例数都将增加。

b) 从内存角度看单例,单例中的Command、Event非常容易造成内存泄露,根源往往是单例而不是事件或command。

c)ANTS 展现的内存图并不全面,经常需要多张图结合看问题。比如,当View是当前页面时,不能看出Lazy<T>产生的问题,而需要跳转到View不是当前页面再看另外的快照。

d)ANTS 的快照图,对一类引用进行了归类,比如stopDownloadCommand并不是ViewModle中唯一的Command,还有reDownloadCommand也引用了View,图上并没有表现出来,在代码中修正了stopDownloadCommand的引用问题,View仍被reDownloadCommand引用而不能释放。

 

posted on 2011-04-17 16:06  思无邪  阅读(2042)  评论(3编辑  收藏  举报

导航