Silverlight内存泄露(六)MEF等Ioc框架引起内存泄露-ExportLifetimeContext
对象的创建可以使用new,也可以使用IOC架如:castle、MEF等,IOC创建的对象的生命周期,可能IOC负责管理,使用框架的开发者如果不弄清楚可能会造成内存泄露问题。
这些内存泄露问题并不是IOC框架的bug,只是开发者使用不当或者不注意造成的内存泄露问题。
以MEF为例说明我碰到的两种内存泄露问题。
内存泄露系列阅读提示:
一摸一样的对象图,有时候我们可以认为它是内存泄露,有时候又认为它不是内存泄露,这一切只是由于上下文不同,这一系列文章中ANTS Memoery Profle截图都是有特定上下文,单独看完全没有意义。如何确定是内存泄露?可以参考前面的文章。
对象以图的形式存在,Ants Memory Profile为了分析方便把这些图处理为树,让我们可以把注意力集中到分析的对象。但我们必须明白内存中对象关系构成图,也就是说ANTS的树状图只是内存中对象分布的一个局部,分析内存泄露时必须有全局观念,需要相关的几张图一起看,即使一张图也要整体看,这样才能分析内存泄露问题。
由于看一张图没什么意义,如果把多张图都贴出来,这文章就太难写了,即使多张图都贴出来,也不一定能表达清楚,分析内存泄露最重要的是经验。接下来的几篇会减少甚至不用ANTS图。
ExportLifetimeContext使用不当造成内存泄露
ExportLifetimeContext 需要调用Dispose方法释放由MEF管理的对象,否则对象不会被释放。
MSDN:
Disposing of a ExportLifetimeContext(Of T) object calls the referenced method to release its associated export.
Call Dispose when you are finished using the ExportLifetimeContext(Of T). The Dispose method leaves the ExportLifetimeContext(Of T) in an unusable state. After calling Dispose, you must release all references to the ExportLifetimeContext(Of T) so the garbage collector can reclaim the memory that theExportLifetimeContext(Of T) was occupying.
可以看到ViewModel存在多个实例,可能出现了内存泄露。
经过分析找出发生内存泄露的对象图:
可以看到MEF的DisposableReflectionComposablePart一直保持着ViewModel的引用,造成不能释放ViewModel。
MVVM使用自定义导航,代码:
public class CompositionNavigationContentLoader : INavigationContentLoader
{
public CompositionNavigationContentLoader()
{
}
[ImportMany(typeof(IView))]
public IEnumerable<ExportFactory<IView, IViewMetadata>> ViewExports { get; set; }
[ImportMany(typeof(IViewModel),RequiredCreationPolicy = CreationPolicy.Any)]
public List<ExportFactory<IViewModel, IViewModelMetadata>> ViewModelExports { get; set; }
[ImportMany("lazy", typeof(IViewModel), RequiredCreationPolicy = CreationPolicy.Any)]
public List<Lazy<IViewModel, IViewModelMetadata>> LazyViewModelExports { get; set; }
public IAsyncResult BeginLoad(Uri targetUri, Uri currentUri, AsyncCallback userCallback, object asyncState)
{
var viewModelMapping = ViewModelExports.FirstOrDefault(o => o.Metadata.Key.Equals(relativeUri.Host, StringComparison.OrdinalIgnoreCase));
//ViewModel
var viewModelFactory = viewModelMapping.CreateExport();
viewModel = viewModelFactory.Value as IViewModel;
viewModelFactory.Dispose();//释放对象
//View
var viewFactory = viewMapping.CreateExport();
view = viewFactory.Value as Control;
viewFactory.Dispose(); //释放对象
//绑定、导航
view.DataContext = viewModel;
var values = viewModelMapping.Metadata.GetArgumentValues(targetUri);
viewModel.OnNavigated(values);
}
}
如果不加上viewModelFactory.Dispose();
对象MEF创建的对象不会被回收,VIew、ViewModel及其引用的资源都一直保持到程序关闭。
结论
a) 不要盲目使用第三方框架。
b) 内存泄露都有上下文,只有特定上下文中托管代码才可能产生内存问题。
c)注意由IOC创建的对象生命周期,如果IOC创建的对象由容器管理生命期,可能需要调用IOC提供的相关方法执行对象的销毁。