Understanding Part Lifetime

Mar 15, 2010 at 4:39 PM

I am trying to profile my application and have found that no type created through MEF (or new & SatisfyImportsOnce on) is ever released. Reading through the lifetime document (http://mef.codeplex.com/wikipage?title=Parts%20Lifetime&referringTitle=Guide) I expected these to go away when I release my reference to it, however my profiler (.NET Memory Profiler by SciTech) shows that it is being held by MEF, specifically by a ReflectionComposablePart object. Is this expected on a NonShared part? Do we always have to call ReleaseExport, even on nonshared items and items passed into SatisfyImportsOnce? I've been experimenting with a test class below:

    [PartCreationPolicy(CreationPolicy.NonShared)]
    [Export(typeof(TestMEF))]
    public class TestMEF
    {
        public TestMEF()
        {
        }

        public TestMEF(string id)
        {
            ID = id;
        }

        public string ID { get; set; }

        [Import]
        public CompositionContainer Host { get; set; }
    }

 Test code:

        private void NewAndSatisfy_Click(object sender, RoutedEventArgs e)
        {
            _testMEF = new TestMEF("NewAndSatisfy");
            Host.SatisfyImportsOnce(_testMEF);
        }

        private void GetExported_Click(object sender, RoutedEventArgs e)
        {
            _testMEF = Host.GetExportedValue<TestMEF>();
            _testMEF.ID = "GetExported";
        }
        private void Null_Click(object sender, RoutedEventArgs e)
        {
            _testMEF = null;
        }

Developer
Mar 15, 2010 at 7:42 PM

Out of curiosity are you doing your testing on the codeplex version of MEF? If so then there is a know memory leak in both of your test cases above and you would need to call ReleaseExport in order to cleanup the memory.

In order to prevent the leak and allow the GC to cleanup the references our engine uses a ConditionalWeakTable which was added in .Net 4.0. However in our codeplex version which targets .Net 3.5 we've implemented a ConditionalWeakTable which has known bugs, namely circular references will never be cleaned up (which we do hit in our engin) and we cannot fix this problem in .Net 3.5 because it actually requires a change to the CLR which was added in .Net 4.0.

So to summarize the scenarios you list above will work fine in .Net 4.0 but in our codeplex .Net 3.5 drop you will have to call GetExport/ReleaseExport to properly cleanup everything on an individual export basis.

Mar 15, 2010 at 8:06 PM

Yes, we're using .NET 3.5. Thanks for the feedback, I'll put off further work on this until we upgrade to .NET 4.0.

Jun 15, 2010 at 3:19 PM
weshaggard wrote:

So to summarize the scenarios you list above will work fine in .Net 4.0 but in our codeplex .Net 3.5 drop you will have to call GetExport/ReleaseExport to properly cleanup everything on an individual export basis.

We are using CompositionContainer.ComposeParts to create our (nonshared) objects. We do this from a global CompositionContainer. As pointed out in this thread the CompositionContainer stores a reference to the parts it composes through ReflectionComposablePart._cachedInstance causing a memory leak in our case. 

I tried to use GetExport/ReleaseExport instead, but GetExport (unlike ComposeParts) always creates the same instance of the exported object, while we need to create new instances each time.

Developer
Jun 15, 2010 at 9:19 PM

To help clear this up you would use ReleaseExport when you use GetExport to retrieve it. If you are actually calling ComposeParts what happens underneath is it creates a CompsitionBatch adds your parts to it and then calls container.Compose. If you want to undo this behavior you would need to directly work with the CompositionBatch so at a later point you can remove the part that was added. 

Something like:

CompositionBatch batch = new CompositionBatch();
ComposablePart storedPart = batch.AddPart(<object you want composed>);
container.Compose(batch);

.... Later after you are done with it and you want to explicitly clean it up (note you don't have to do this if you are ready to dispose the container because it will cleanup the objects on container dispose).

CompositionBatch batch = new CompositionBatch();
batch.RemovePart(storedPart);
container.Compose(batch);

 

 

Oct 6, 2010 at 11:51 AM
Edited Oct 6, 2010 at 12:17 PM

Hi.

I've encountered this memory leak in my silverlight application too.

When calling CompositionInitializer.SatisfyImports(this), a ReflectionComposablePart is created, that keeps an instance of the passed object (this._cachedInstance = attributedPart).

I'm using the latest drop on CodePlex (preview 9) but I believe that the same code is used in the SL4 release code (according to reflector).

Is there a way around that ? It's a really big issue in Silverlight app because event aggregator's event handler will keep firing until the instance is released, leading to unexpected results.

EDIT: 

I just want to clarify something. The memory leak concerns the object that imports other parts, as opposed to the exported parts themselves. In other words, it is the 'this' of the CompositionInitializer.SatisfyImports(this) that leaks.

Nov 5, 2010 at 5:04 PM
clement_911 wrote:

EDIT: 

I just want to clarify something. The memory leak concerns the object that imports other parts, as opposed to the exported parts themselves. In other words, it is the 'this' of the CompositionInitializer.SatisfyImports(this) that leaks.

Have you found a solution for that problem?

Nov 5, 2010 at 11:00 PM

@aldokocha

No I haven't. I don't have time to fix the problem in the source code myself.

I'm hoping that the next MEF drop will have a fix for it. But I'm not sure if, and when, that would be.

Nov 17, 2010 at 10:55 PM

Hi,

We're still evaluating possible solutions to this problem.

Nov 17, 2010 at 11:56 PM

Thanks haveriss.

It's good to know that it's being looked into.

As I said above: "It's a really big issue in Silverlight app because event aggregator's event handler will keep firing until the instance is released, leading to unexpected results."

Thanks for listening, and please keep us posted with any further development.

 

 

Nov 18, 2010 at 7:24 AM

Having the same problem here with a Silverlight 4 app leaking ViewModel instances. I run "AppContext.Container.ComposeParts(this);" in my base ViewModel class (where AppContext is a static class that holds a reference to the MEF container).

Nov 18, 2010 at 7:55 AM

I think I fixed my problem by switching from calling ComposeParts(this); to SatisfyImportsOnce(this);

Nov 23, 2010 at 3:51 PM

I've run into the same problem yesterday, figured out this morning, spent too many hours tracking it down to the CompositionInitializer.SatisfyImports(this) call in the views constructor.

On my simple test, AttributedModelServices.SatisfyImportsOnce appears to fix the problem.

Nov 24, 2010 at 4:08 PM

I found this link:

http://mef.codeplex.com/wikipage?title=CompsitionInitializer

Which has a very important caveat at the end:

  • All parts created with CompositionInitializer are held around by MEF until the application shuts down. Thus it is not ideal to use for composing transient multiple-instance parts. For these cases a better choice is to use ExportFactory

    Jay 

     

  • Nov 25, 2010 at 6:15 AM

    That is one thing that the parts are being held around.

    But as I am saying above : "The memory leak concerns the object that imports other parts, as opposed to the exported parts themselves. In other words, it is the 'this' of the CompositionInitializer.SatisfyImports(this) that leaks."