热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

MEF引起的内存泄露

也许你编程的时候很小心,注意不引起内存泄露,例如不要被全局Static的变量引用上,注意Singleton的static引用,注意EventHandler注销,注意IDisposa

也许你编程的时候很小心,注意不引起内存泄露,例如不要被全局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的源码。

内存泄露分析过程

内存分析工具很多,例如Windbg, Redgate 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();
 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程序内存泄露>以及<如何写高性能的托管程序>
  • How to detect and avoid memory and resources leaks in .NET applications
  • Writing High-Performance Managed Applications : A Primer
有关.NET的自动内存管理机制、GC机制,垃圾回收原理等深层次内容,请仔细阅读下面的内容:
  • 买书《CLR Via C#(3rd Edition)》,里面有《Memory Management》这一章专门讲述了.NET CLR的自动内存管理和垃圾回收机制
  • CodeProject上的文章《Memory Management Misconceptions》有助你深入理解Root, Generation 0, 1…

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

MEF引起的内存泄露


推荐阅读
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • 本文介绍了C#中数据集DataSet对象的使用及相关方法详解,包括DataSet对象的概述、与数据关系对象的互联、Rows集合和Columns集合的组成,以及DataSet对象常用的方法之一——Merge方法的使用。通过本文的阅读,读者可以了解到DataSet对象在C#中的重要性和使用方法。 ... [详细]
  • 后台获取视图对应的字符串
    1.帮助类后台获取视图对应的字符串publicclassViewHelper{将View输出为字符串(注:不会执行对应的ac ... [详细]
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • 本文介绍了使用AJAX的POST请求实现数据修改功能的方法。通过ajax-post技术,可以实现在输入某个id后,通过ajax技术调用post.jsp修改具有该id记录的姓名的值。文章还提到了AJAX的概念和作用,以及使用async参数和open()方法的注意事项。同时强调了不推荐使用async=false的情况,并解释了JavaScript等待服务器响应的机制。 ... [详细]
  • Java实战之电影在线观看系统的实现
    本文介绍了Java实战之电影在线观看系统的实现过程。首先对项目进行了简述,然后展示了系统的效果图。接着介绍了系统的核心代码,包括后台用户管理控制器、电影管理控制器和前台电影控制器。最后对项目的环境配置和使用的技术进行了说明,包括JSP、Spring、SpringMVC、MyBatis、html、css、JavaScript、JQuery、Ajax、layui和maven等。 ... [详细]
  • 本文讨论了Alink回归预测的不完善问题,指出目前主要针对Python做案例,对其他语言支持不足。同时介绍了pom.xml文件的基本结构和使用方法,以及Maven的相关知识。最后,对Alink回归预测的未来发展提出了期待。 ... [详细]
  • Windows下配置PHP5.6的方法及注意事项
    本文介绍了在Windows系统下配置PHP5.6的步骤及注意事项,包括下载PHP5.6、解压并配置IIS、添加模块映射、测试等。同时提供了一些常见问题的解决方法,如下载缺失的msvcr110.dll文件等。通过本文的指导,读者可以轻松地在Windows系统下配置PHP5.6,并解决一些常见的配置问题。 ... [详细]
  • Mac OS 升级到11.2.2 Eclipse打不开了,报错Failed to create the Java Virtual Machine
    本文介绍了在Mac OS升级到11.2.2版本后,使用Eclipse打开时出现报错Failed to create the Java Virtual Machine的问题,并提供了解决方法。 ... [详细]
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • 本文介绍了在SpringBoot中集成thymeleaf前端模版的配置步骤,包括在application.properties配置文件中添加thymeleaf的配置信息,引入thymeleaf的jar包,以及创建PageController并添加index方法。 ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • 本文讲述了作者通过点火测试男友的性格和承受能力,以考验婚姻问题。作者故意不安慰男友并再次点火,观察他的反应。这个行为是善意的玩人,旨在了解男友的性格和避免婚姻问题。 ... [详细]
  • 《数据结构》学习笔记3——串匹配算法性能评估
    本文主要讨论串匹配算法的性能评估,包括模式匹配、字符种类数量、算法复杂度等内容。通过借助C++中的头文件和库,可以实现对串的匹配操作。其中蛮力算法的复杂度为O(m*n),通过随机取出长度为m的子串作为模式P,在文本T中进行匹配,统计平均复杂度。对于成功和失败的匹配分别进行测试,分析其平均复杂度。详情请参考相关学习资源。 ... [详细]
author-avatar
awrjftyitik
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有