【问题标题】:MEF keeps reference of NonShared IDisposable parts, not allowing them to be collected by GCMEF 保留对 NonShared IDisposable 部分的引用,不允许它们被 GC 收集
【发布时间】:2012-02-05 23:05:16
【问题描述】:

我在 MEF 的部分生命周期中遇到了一些问题,导致我的 Prism 应用程序出现内存泄漏。

我的应用程序导出视图和视图模型,其中 PartCreationPolicy 设置为 CreationPolicy.NonShared。视图和视图模型分别继承自ViewBaseViewModelBase,它们实现了IDisposable

现在,由于我的部件实现了IDisposable,容器保留了对它们的引用,这导致垃圾收集器不会释放它们。根据MEF documentation on part lifetime,这是设计使然:

除非满足以下条件之一,否则容器不会保存对其创建的部件的引用:

  • 该部分标记为Shared
  • 部分实现IDisposable
  • 一个或多个导入配置为允许重组

我怎样才能让 MEF 不保留对这些部分的引用?是否有一个属性可以让 MEF 知道我不希望它保留对我的部分的引用,即使它实现了IDisposable

上面文章中讨论的两种策略对我来说似乎都不是好的解决方案:

  • ReleaseExport 需要Export 对象作为参数,我不知道如何提供。我有我的视图实例,但我无法知道用于创建视图的合同是什么。如果ReleaseExport 有一个可以接收容器创建的任何对象的重载,那就太好了。
  • 使用子容器似乎也不是一个自然的选择。

任何帮助将不胜感激。

【问题讨论】:

    标签: .net memory-leaks prism mef object-lifetime


    【解决方案1】:

    您应该通过导入的ExportFactory<T> 创建这些实例。然后,您将拥有必要的控制权,通过ExportLifetimeContext&lt;T&gt;.Dispose() 处理它们。

    但是,这仅在下一个 .NET 版本 (4.5) 或 codeplex 上的最新 MEF 预览版中可用。 (在旧版本的 MEF 中,相同的功能被实现为示例,称为 PartCreator,如 blog post 中所述。)

    【讨论】:

    • 我没有在我的问题中提到它,但我正在使用 Prism 来创建我的视图,所以我无法真正访问代码的那部分来检索 ExportFactory 对象。即使我可以,也存在从代码中我想要处理视图的位置访问工厂对象的问题,并为每个NonShared 对象使用ExportFactory,这非常不直观,看起来不像一个很好的做法。
    • @Lester:您应该只对需要由导入器显式创建/处置的对象使用 ExportFactory(这使得创建生命周期比 MEF 容器更短的对象)。我不是说您现在应该在任何有非共享导出的地方使用 ExportFactory。
    【解决方案2】:

    当您实现IDisposable 时,您的意思是说应该以确定的方式清理类型(通过调用IDisposable.Dispose 而不是在垃圾收集器确定是时候随机地清理。

    在您的情况下,视图模型只会在您处置容器时处置,这可能不是您想要做的。为了解决这个问题,我看到了两种可能的解决方案:

    • 不要在你的视图模型上实现IDisposable。显然你并不关心它们什么时候被清理,所以为什么要让它们IDisposable

    • 您可以使用共享视图模型工厂类,而不是让容器创建每个非共享视图模型。然后,您可以将该类注入到视图模型的所有者中,以允许所有者显式地创建视图模型。大概这些所有者也知道何时处置视图模型。

    基本上,如果某些东西是一次性的,那么在您的代码中也应该是一个明智的点,您需要在其中处理一次性的东西。

    【讨论】:

    • 我相信你会同意,我实施IDisposable 是为了尽早处置一个对象,并由于MEF容器的实施而达到完全相反的效果,这有点愚蠢.不在我的虚拟机上实现IDisposable 不是一个真正的选择,因为我需要像从复合命令中注销一样进行清理——如果我不这样做,虚拟机就不会被 GC 处理。我的观点负责处理他们的虚拟机,但无论调用Dispose() 都不会从容器中删除对对象的引用,所以无论如何这都无济于事。
    • 我认为使用我的第二个建议将视图模型工厂注入每个视图而不是 MEF 创建的视图模型应该可以解决您的问题。显然,为每个视图模型创建工厂变得相当乏味,但在使用当前版本的 MEF 进行依赖注入时,我发现自己经常这样做。
    【解决方案3】:

    所有其他答案都提供了规避此问题的好方法,但我最终做的是使用我自己的自定义界面ICleanup,而不是IDisposable。当然,这可能并不适合所有人。

    【讨论】:

      【解决方案4】:

      除非 Prism 支持视图对象的某种生命周期,否则这里没有解决方案,除了从视图公开的接口列表中删除 IDisposable

      有三种 MEF 方法来处理这个问题,其他响应者都提到了:

      • ExportFactory&lt;T&gt;
      • 儿童容器
      • ReleaseExport()

      所有这些都需要对请求原始导出的代码部分进行一些工作 - 在这种情况下是 Prism 中的代码。这是有道理的,因为使用对象的代码不需要知道它是如何以及何时创建的。

      MEF 中没有ReleaseExportedObject(),因为多个(例如属性)导出可以返回相同的值;在逻辑上可能提供,但增加的复杂性使得 MEF 在可预见的将来不太可能解决它。

      希望这会有所帮助;我已将此问题重新标记为“棱镜”,因为我确信 Prism 社区中的其他人会遇到此问题并能够提供建议。

      【讨论】:

      • 感谢您的回复和欢迎重新标记。我想与 Prism 以及 ExportFactory 的集成是正确的方法,尽管对于“不要将我添加到容器”这样的简单请求来说,这似乎有点过头了。我还没有放弃——我仍在寻找更简单、更优雅的解决方案。
      猜你喜欢
      • 2010-11-14
      • 1970-01-01
      • 2016-04-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多