Object lifetime of NonShared IDisposable parts

Jan 9, 2012 at 11:58 AM

I've also posted this question on stackoverflow. This may also be related to this discussion.

I've encountered somewhat of a problem in MEF's part lifetime which causes memory leaks in my application.

My application exports views and viewmodels with the PartCreationPolicy being set to CreationPolicy.NonShared. The views and viewmodels inherit from ViewBase and ViewModelBase respectively, which implements IDisposable.

Now, since my parts implement IDisposable, a reference to them is kept by the container, which causes them to not be released by the garbage collector. According to MEF documentation on part lifetime, this is by design:

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

How then can I make MEF not keep a reference to these parts? Both of the strategies discussed in the above article don't seem like good solutions for me:

  • ReleaseExport requires an Export object as a parameter, which I don't know how to provide. I have the instance of my view, but there's no way for me to know what was the contract that was used to create the view. It would've been great if there was an overload for ReleaseExport which could receive any object created by the container.
  • Using a child container doesn't seem like a natural option either.

Any help will be greatly appreciated.

Jan 10, 2012 at 9:01 AM

Why do you think using child container is not a good solution?

When the container is disposed, all of disposable parts will be disposed too. You can control the lifetime of the parts by controlling lifetime of the containers. This can be done by creating nested containers e.g. one with global (application) lifetime and another with per-request (or whatever you like) lifetime. You should manually create per-request container for each request and dispose it at the end of that request or whenever you want. You can find an implementation of this for MVC in MEF 2 and MefContrib. There should be other implementations for MVVM and Silverlight too.

Jan 10, 2012 at 12:28 PM
Edited Jan 10, 2012 at 12:31 PM

All I want is for MEF to not hold a reference to my object even though it implements IDisposable so that it could be collected by the garbage collector. If it wants to keep a reference, why not make it a weak reference? MEF container is the only thing keeping my views in memory.

Why do I have to go through the trouble of creating child containers for my views (which is even more complicated since prism creates my views), have these containers available for the class I want to dispose of them from and call dispose on the container? This is a major pain for such a simple request.

Jan 10, 2012 at 6:14 PM
Edited Jan 10, 2012 at 6:18 PM

I'm not familiar with MVVM and Prism so I can't help you much about them. But for MEF I can think of these solutions:

Firstly If you can use lazy import, you can pass that Lazy<T> object to ReleaseExport and get rid of unused parts.

Secondly as far as I know there are two methods to change this behavior of MEF container:

* Create a custom catalog and in its GetExports methods return a custom ComposablePartDefinition which doesn't create a disposable ComposablePart even if the part is disposable. This prevents CatalogExportProvider from storing the ComposablePart for later disposal.

public class CustomCatalog : ComposablePartCatalog
{
    private readonly ComposablePartCatalog _innerCatalog;

    public CustomCatalog(ComposablePartCatalog catalog)
    {
        _innerCatalog = catalog;
    }

    public override IEnumerable<Tuple<ComposablePartDefinition, ExportDefinition>> GetExports(ImportDefinition definition)
    {
        var exports = _innerCatalog.GetExports(definition);
        return exports.Select(e => new Tuple<ComposablePartDefinition, ExportDefinition>(
            new CustomComposablePartDefinition(e.Item1), e.Item2));
    }

    public override IQueryable<ComposablePartDefinition> Parts
    {
        get
        {
            return _innerCatalog.Parts
                .Select(p => new CustomComposablePartDefinition(p))
                .AsQueryable();
        }
    }
}


public class CustomComposablePartDefinition : ComposablePartDefinition
{
    private readonly ComposablePartDefinition _innerPartDefinition;

    public CustomComposablePartDefinition(ComposablePartDefinition innerPartDefinition)
    {
        _innerPartDefinition = innerPartDefinition;
    }

    public override ComposablePart CreatePart()
    {
        var innerPart = _innerPartDefinition.CreatePart();
        if (ReflectionModelServices.IsDisposalRequired(_innerPartDefinition) && 
            _innerPartDefinition.ExportDefinitions.Any(ed => ed.ContractName == "Do Not Reference Me"))
        {
            return new NotDisposableComposablePart(innerPart);
        }
        return innerPart;
    }

    public override IEnumerable<ExportDefinition> ExportDefinitions
    {
        get { return _innerPartDefinition.ExportDefinitions; }
    }

    public override IEnumerable<ImportDefinition> ImportDefinitions
    {
        get { return _innerPartDefinition.ImportDefinitions; }
    }

    public override IDictionary<string, object> Metadata
    {
        get { return _innerPartDefinition.Metadata; }
    }

    public override string ToString()
    {
        return _innerPartDefinition.ToString();
    }
}

NotDisposableComposablePart is a simple wrapper class which derives from ComposablePart and because it doesn't implement IDisposable, CatalogExportProvider doesn't store it.

 

* Another way is creating a custom ExportProvider which changes default behavior of CatalogExportProvider. This is somewhat difficult because the code responsible for storing disposable parts are not accessible and easily overridable.

 

NOTE 1: I have not tested these methods but I hope they could solve your problem.

NOTE 2: These methods may stop working in future release of MEF.

I think customizing Prism may be a better solution so it's better to ask this question in Prism discussions too.

Jan 10, 2012 at 6:51 PM

Hi,

I'm in the process of migrating WPF MVVM application to use MEF instead of custom made ServiceLocator.

As far as I understand MEF fit very well in the host application and should be integrated with the navigation logic.

If you are using MEF1 the only way to go is child container.

If you are using MEF2 you can choose between child container and CompositionScopeDefinition.

Either way you will have to manage the scope of the container, which is a good thing. Because it is the responsibility of the container to create new instances it should also be its responsibility to dispose them, so I don't see any problem with the container keep reference to your parts as long as you notify the container when it is no longer required.

 

Ido

May 31, 2013 at 11:14 PM
I recomment everyone with this issue to look at this blog post with the modification from my comment.

http://toreaurstad.blogspot.ca/2012/09/freeing-up-memory-used-by-mef.html