也许你编程的时候很小心,注意不引起内存泄露,例如不要被全局Static的变量引用上,注意Singleton的static引用,注意Event Handler注销,注意IDisposable接口实现,而且正确实现了IDisposable。但或许你还是有内存泄露,为何?因为你的IDisposable接口根本没有被触发!为什么?参考MSDN这个页面的”Dispose method not invoked ”章节。还有其它的内存泄露原因,比如第三方组件或框架,框架本身的内存泄露问题,已经框架本身有LifetimeManagement对象生命周期管理机制。例如我今天要说的MEF引起的内存泄露。

实现IDisposable接口,却导致没有触发,这是何故?

看看MEF对象生命周期管理机制说明,里面有这么一段话:

Container and parts references

We believe that .Net Garbage Collector is the best thing to rely on for proper clean up. However, we also need to provider a container that has a deterministic behavior. Thus, the container will not hold references to parts it creates unless one of the following is true:

  1. The part is marked as Shared
  2. The part implements IDisposable
  3. One or more imports is configured to allow recomposition

For those cases, a part reference is kept. Combined with the fact that you can have non shared parts and keep requesting those from the container then memory demand can quickly become an issue. In order to mitigate this issue you should rely on one of the following strategies discussed in the next two topics.

原来在MEF中,对象构造策略为Shared,实现IDisposable接口的,和允许recomposition的,都会被全局container引用,也就是说不释放。具体可以去看MEF的源码。

内存泄露分析过程

内存分析工具很多,例如WindbgRedgate ANTS MemoryProfiler, 以及.NET Memory Profiler。我是用的Redgate的,大家可以从下面的图中看到为何这个ViewModel被引用着。根据这个图,去看MEF的源码,就ok了。

MEF Parts Lifetime (zz from: http://mef.codeplex.com/wikipage?title=Parts%20Lifetime)

It’s very important that one understand a parts lifetime within a MEF container and its implications. Given that MEF focuses on open ended applications this becomes especially important as the application authors won’t have control over the set of parts once the app ships and third party extensions come to play. Lifetime can be explained as being the desired “shareability” of a part (transitively, its exports) which translates to the policy that controls when a new part is created as well as when the part will be shut down or disposed.

Shared, Non Shared and ownership

The “shareability” of a part is defined through the CreationPolicy set (class level) using the PartCreationPolicyAttribute. The following values are supported:

  • Shared: the part author is telling MEF that at most one instance of the part may exist per container.
  • NonShared: the part author is telling MEF that each request for exports of the part will be served by a new instance of it.
  • Any or not supplied value: the part author allows the part to be used as either “Shared” or “NonShared”.

The Creation Policy can be defined on a part using the[System.ComponentModel.Composition.PartCreationPolicyAttribute]:

 1: [PartCreationPolicy(CreationPolicy.NonShared)]
 2: [Export(typeof(IMessageSender))]
 3: public class SmtpSender : IMessageSender
 4: {
 5: }

The container will always have the ownership of parts it creates. In other words, the ownership is never transferred to an actor that requested it by using the container instance (directly) or through an import (indirectly). 
Imports can also define or constraint the creation policy of parts used to supply the import values. All you have to do is specify the CreationPolicy enum value for RequiredCreationPolicy:

 1: [Export]
 2: public class Importer
 3: {
 4: [Import(RequiredCreationPolicy=CreationPolicy.NonShared)]
 5: public Dependency Dep { get; set; }
 6: }

This is a useful for scenarios where the “shareability” of a part is relevant for the importer. By default, the RequiredCreationPolicy is set to Any, so Shared and NonShared parts can supply the values..

Disposing the container

A container instance is generally the lifetime holder of parts. Part instances created by the container have their lifetime conditioned to the container’s lifetime. The way to signal the end of the container lifetime is by disposing it. The implications of disposing a container are:

  • Parts that implement IDisposable will have the Dispose method called
  • Reference to parts held on the container will be cleaned up
  • Shared parts will be disposed and cleaned up
  • Lazy exports won’t work after the container is disposed
  • Operations might throw System.ObjectDisposedException

Container and parts references

We believe that .Net Garbage Collector is the best thing to rely on for proper clean up. However, we also need to provider a container that has a deterministic behavior. Thus, the container will not hold references to parts it creates unless one of the following is true:

  • The part is marked as Shared
  • The part implements IDisposable
  • One or more imports is configured to allow recomposition

For those cases, a part reference is kept. Combined with the fact that you can have non shared parts and keep requesting those from the container then memory demand can quickly become an issue. In order to mitigate this issue you should rely on one of the following strategies discussed in the next two topics.

Scoped operations and early reclaim of resources

Some common kinds of applications like web apps and windows services vary greatly from desktop applications. They are more likely to rely on batched and short lived operations. For example, a windows service might watch a directly and once a pre-determined number of file is present, start a batching operation that transforms those files into another format. Web operations may be scoped by per-web-request operations. 
For those scenarios you should either use child containers (explained in the next topic) or release early the object graph. The latter allows the container to dispose and clear out references to non shared parts in the object graph – until it reaches a shared part. 
In order to early release the object graph you need to call the method ReleaseExport exposed by the CompositionContainer:

 1: var batchProcessorExport = container.GetExport<IBatchProcessor>();
 2:  
 3: var batchProcessor = batchProcessorExport.Value;
 4: batchProcessor.Process();
 5:  
 6: container.ReleaseExport(batchProcessorExport);
 

The figure below depicts an object graph and show what parts would be released (references removed, disposed) and the ones that would be left untouched: 
 
As the root part is just non shared no reference was being kept by the container, so it is basically a no-operation. We proceed traversing the graph checking the exports served to the root part. Dep 1 is both non shared and disposable, so the part is disposed and its reference is removed from the container. The same happens with Dep 2, however, the export used by Dep is left untouched as it is shared – so other parts may be using it. 
Note that the implementation traverses the graph in a depth-first way.

Container hierarchies

Another way to approach the same problem is to use container hierarchies. You can create containers and connect them to a parent container, making them child containers. Note that unless you provide a different catalog to the child container, it wouldn’t be of much help as instantiation will continue to happen on the parent. 
Hence, what you should do is either filter the parent catalog based on a criterion that divides the set of parts that should be created on the parent container from those that should be created on the child, or you should specify a completely new catalog that expose a set of parts that should be created on the child container. As the child is expected to be short lived, parts created in it will be released and disposed earlier. 
A common approach is to have Shared parts created on the parent container and Non Shared on the child container. As Shared parts may depend on exports supplied by Non Shared, then the main catalog will have to contain the whole set of parts whereas the child container should have a filtered view of the main catalog with only the non shared parts. 
 
For more information on this topic please check Filtering Catalogs

Disposal ordering

Disposal ordering is not guaranteed in any way. That means that you should not try to use an import in your Dispose method. For example:

 1: [Export]
 2: public class SomeService : IDisposable
 3: {
 4: [Import]
 5: public ILogger Logger { get; set; }
 6: 
 7: public void Dispose()
 8: {
 9: Logger.Info("Disposing"); // might throw exception!
 10: }
 11: }
Using the imported logger instance on your dispose method implementation may be a problem as the implementation of the ILogger contract may also be disposable, and as such may have been disposed already. 

AddPart/RemovePart

Not every part is created by the container. You can also add and remove parts from it. This process triggers composition and may start creating parts to satisfy dependencies of the part added recursively. When the part added is removed MEF is smart enough to reclaim the resources and dispose the non shared parts used by the part added. 
Note: that MEF will never take ownership of an instance supplied by you, but it does have the ownership of part it creates to satisfy your instance’s imports.

 1: using System;
 2: using System.ComponentModel.Composition;
 3: using System.ComponentModel.Composition.Hosting;
 4: using System.ComponentModel.Composition.Primitives;
 5:  
 6: class Program
 7: {
 8: static void Main(string[] args)
 9: {
 10: var catalog = new AssemblyCatalog(typeof(Program).Assembly);
 11: var container = new CompositionContainer(catalog);
 12: var root = new Root();
 13:  
 14: // add external part
 15: container.ComposeParts(root);
 16:  
 17: // ... use the composed root instance
 18:  
 19: // removes external part
 20: batch = new CompositionBatch();
 21: batch.RemovePart(root);
 22: container.Compose(batch);
 23: }
 24: }
 25:  
 26: public class Root
 27: {
 28: [Import(RequiredCreationPolicy = CreationPolicy.NonShared)]
 29: public NonSharedDependency Dep { get; set; }
 30: }
 31:  
 32: [Export, PartCreationPolicy(CreationPolicy.NonShared)]
 33: public class NonSharedDependency : IDisposable
 34: {
 35: public NonSharedDependency()
 36: {
 37: }
 38:  
 39: public void Dispose()
 40: {
 41: Console.WriteLine("Disposed");
 42: }
 43: }
 

深入思考和继续阅读

通常.NET程序的内存泄露原因:

  • Static references
  • Event with missing unsubscription
  • Static event with missing unsubscription
  • Dispose method not invoked
  • Incomplete Dispose method

有关如何避免.NET程序的内存泄露,请仔细阅读MSDN这两篇文章,详细讲述了<如何检测.NET程序内存泄露>以及<如何写高性能的托管程序>

有关.NET的自动内存管理机制、GC机制,垃圾回收原理等深层次内容,请仔细阅读下面的内容:

http://www.cnblogs.com/Mainz/archive/2011/09/10/2173172.html

MEF引起的内存泄露的更多相关文章

  1. MEF等Ioc框架引起内存泄露-PartCreationPolicy

    对象的创建可以使用new,也可以使用IOC架如:castle.MEF等,IOC创建的对象的生命周期,可能IOC负责管理,使用框架的开发者如果不弄清楚可能会造成内存泄露问题. 这些内存泄露问题并不是IO ...

  2. java: web应用中不经意的内存泄露

    前面有一篇讲解如何在spring mvc web应用中一启动就执行某些逻辑,今天无意发现如果使用不当,很容易引起内存泄露,测试代码如下: 1.定义一个类App package com.cnblogs. ...

  3. 查看w3wp进程占用的内存及.NET内存泄露,死锁分析

    一 基础知识 在分析之前,先上一张图: 从上面可以看到,这个w3wp进程占用了376M内存,启动了54个线程. 在使用windbg查看之前,看到的进程含有 *32 字样,意思是在64位机器上已32位方 ...

  4. C++11 shared_ptr 智能指针 的使用,避免内存泄露

    多线程程序经常会遇到在某个线程A创建了一个对象,这个对象需要在线程B使用, 在没有shared_ptr时,因为线程A,B结束时间不确定,即在A或B线程先释放这个对象都有可能造成另一个线程崩溃, 所以为 ...

  5. 基于HTML5的WebGL应用内存泄露分析

    上篇(http://www.hightopo.com/blog/194.html)我们通过定制了CPU和内存展示界面,体验了HT for Web通过定义矢量实现图形绘制与业务数据的代码解耦及绑定联动, ...

  6. android:布局、绘制、内存泄露、响应速度、listview和bitmap、线程优化以及一些优化的建议!

    1.布局优化 首先删除布局中无用的控件和层级,其次有选择地使用性能较低的viewgroup,比如布局中既可以使用RelativeLayout和LinearLayout,那我们就采用LinearLayo ...

  7. js内存泄露的几种情况详细探讨

    内存泄露是指一块被分配的内存既不能使用,又不能回收,直到浏览器进程结束.在C++中,因为是手动管理内存,内存泄露是经常出现的事情.而现在流行的C#和Java等语言采用了自动垃圾回收方法管理内存,正常使 ...

  8. 使用Xcode7的Instruments检测解决iOS内存泄露

    文/笨笨的糯糯(简书作者)原文链接:http://www.jianshu.com/p/0837331875f0著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”. 作为一名iOS开发攻城狮, ...

  9. 使用Visual Leak Detector for Visual C++ 捕捉内存泄露

    什么是内存泄漏? 内存泄漏(memory leak),指由于疏忽或错误造成程序未能释放已经不再使用的内存的情况.内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,失去了对该段 ...

随机推荐

  1. 10、LNMP架构

    1LNMP架构概述 1.1.什么是LNMP  LNMP 是一套技术的组合,L = Linux,N = Nginx,M~ = MySQL,P~ = PHP 1.2.LNMP架构是如何工作的 首先Ngin ...

  2. 001-CentOS 7系统搭建Rsyslog+LogAnalyzer解决交换机日志收

    日志功能对于操作系统是相当重要的,在使用中,无论是系统还是应用等等,出了任何问题,我们首先想到的便是分析日志,查找问题原因.自 CentOS 7 开始,我们的 CentOS 便开始使用 rsyslog ...

  3. java8学习之收集器用法详解与多级分组和分区

    收集器用法详解: 在上次已经系统的阅读了Collector收集器的Javadoc对它已经有一个比较详细的认知了,但是!!!它毕境是只是一个接口,要使用的话还得用它的实现类,所以在Java8中有它进行了 ...

  4. flutter 学习路上碰到的错误问题。

    决定还是把碰到的问题进行简单记录吧 19.8.14 错误日志: type '_InternalLinkedHashMap<dynamic, dynamic>' is not a subty ...

  5. 高性能mysql 附录D explain执行计划详解

    EXPLAIN: extended关键字:在explain后使用extended关键字,可以显示filtered列和warning信息.在较旧的MySQL版本中,扩展信息是使用EXPLAIN EXTE ...

  6. C++ 编码导致编译错误

    记录一个问题 平时经常用Notepad++写C++,然后g++编译 最近出现一个莫名其妙的问题 原来是编译时遇到了非法字符 用Notepad++一看,原来文件的编码出现的变化 不是UTF-8或者其他对 ...

  7. R 语言中的简单线性回归

    ... sessionInfo() # 查询版本及系统和库等信息 getwd() path <- "E:/RSpace/R_in_Action" setwd(path) rm ...

  8. 代码自动补全插件CodeMix全新发布CI 2019.7.15|改进CSS颜色辅助

    CodeMix是Eclipse的一款插件,它解锁了VS Code和Code OSS附加扩展的各种技术,支持各种语言. 作为Eclipse开发人员,您再也不必觉得自己已被排除在朋友使用Visual St ...

  9. SpringMvc (注解)中的上传文件

    第一步:导入commons-fileupload-1.3.1.jar 和commons-io-2.2.jar 架包 第二步:在applicationContext.xml中 配置 <bean i ...

  10. Angular 文档中链接的修改路径

    在 Angular 文档程序中的左侧链接的修改路径在哪里? 如下图所示的修改路径. 左侧链接的修改路径在 angular-cn\aio\content\navigation.json 这个文件中. 你 ...