memory releasing for nonshared parts

May 28, 2010 at 10:52 AM

Hi,

I want use MEF for a new Silverlight 4 project.

I created a CompositionContainer and initialize the CompositionHost -> CompositionHost.Initialize(compositionContainer);

For Testing the container is accessible. Now i compose a new importing object (with non shared parts) in 2 variants:

a) _Container.SatisfyImportsOnce(obj);

b) CompositionInitializer.SatisfyImports(obj);

For the first case the object is released like expected, but not in the second case.

Can i configure the Host so that the non shared objects are freed after using ?

 

Thanks Ralf

Jun 4, 2010 at 7:12 PM

I have run into the same problem.  Does this mean that a reference is held to non-part objects when imports are sastified using CompositionInitializer?  That sounds like a memory leak scenario.  

I have mentioned this in my latest blog here: http://u-ix.blogspot.com/

Carlos

Developer
Jun 4, 2010 at 9:56 PM

Unfortunately at this point if you use CompositionInitializer.SatisfyImports(obj) then there isn't any way to cleanup the objects in the container even if obj is collected. The difference between SatisfyImports and SatisfyImportsOnce is that SatisfyImports actually adds a part to the container for the object passed in and in the CompositionInitializer case we didn't actually provide a way for someone to remove that part, where as SatisfyImportsOnce doesn't actually add a part and so NonShared dependencies will be collected when the object that imported them is collected.

CompositionInitializer's original intent was to be used for composing things in the UI (i.e. object created by XAML instead of MEF), in which case pretty much lived the the entire lifetime of the application so it wasn't an issue. However as it turns out people have began using CompositionInitializer for a lot of other scenarios as well which it wasn't explicitly designed for.

If this is causing a huge issue for you can certainly work-around it somewhat by using ExportFactory for things that are heavy on resources so you can explicitly clean them up at some deterministic point. If you have more specific scenarios that you need to work-around feel free to post them and I will attempt to see if there is some sort of work-around I can come up with.

Jun 6, 2010 at 8:04 PM

Carlos, Ralf

Composition Initializer was designed for UI scenarios, but it was not designed primarily for cases where your app shell has a set of parts that are composed usually at startup time through MEF. The parts being imported may not even be visual, they may be services/business rules, view models, etc. It also supports recomposition where new parts can show up in the future based on downloading additional XAPs through DeploymentCatalog. Because we want to allow recomposition to occur, we hold on the part so we can recompose it later.

What is was not designed for (at least in V1) is to handle scenarios where you are creating / destroying many instances of parts throughout the lifetime of the app. This can sometimes bit you in ways you may not expect. For example you might try to use MEF with Silverilght Navigation, and think to yourself, it's just a page so I can compose it right? The navigation framework by default creates new instances of each page each time you navigate. Thus, if you do use CI in a Page you will find that you have loose pages hanging around in memory. Not ideal, we know. One simple work around in the navigation case is that the Nav framework allows turning on caching, thus reusing the instances over and over again rather than creating them each time.

A general alternative approach to newing up new instances is to use ExportFactory<T> as Wes mentioned. With it you can create a new things on the fly which will not be automatically held around in the container. For example, let's say I have an OrdersController that needs to create new OrderViewModels (through MEF) each time an Order is created. Your controller can import an ExportFactory<OrderViewModel> to do that. There is an example of this in our wiki here: http://mef.codeplex.com/wikipage?title=PartCreator&referringTitle=Guide

The MEF team (I am no longer there) is looking into a solution to the memory leak you are experience when using SatisfyImports for instancing scenarios:

  • One option being considered would be to have an additional SatisfyImportsOnce method on CompositionInitializer which when called would not hold the part alive, but would also not support recomposable imports (though it does support recomposition on the imports of the imports :-) )
  • Another option would be to add an overload to SatisfyImports with a boolean allowRecomposition which in the default overload would be set to false, thus not keeping the part alive.
  • A third one I just thought of would be for us to look at the imports and ONLY if they have recomposable imports, then we hold on to the part. That would be less explicit.

Which one would you want?

I went ahead and whipped up a light wrapper of CI to allow this overload. You can access the code here: http://codepaste.net/mo7afq 

If you look in the code you will see a CompositionInitializerEx and CompostionHostEx. (I hate the Ex name but due to the existing classes there was no way to easily remove the ambiguity). Anyway if you use the wrapper by default you will not hold anything alive.

Feel free to use it / blog on it.

Thanks

Glenn

Aug 10, 2010 at 7:33 PM

Will using these Ex types work for components implementing IDisposable or will MEF still hold on to references to those? If MEF will hold on to those components, what would be the best way to get the container to release them? Looking at the ReleaseExport member, its not self evident how to get a reference to an Export which is what ReleaseExport needs.

Aug 10, 2010 at 8:19 PM

MEF holds a weak reference to parts with a non-shared creation policy that are disposable. If they are shared however MEF will hold on to them regardless of if they are disposable or not.

Aug 10, 2010 at 8:22 PM

@blackbelt is your question around using the CompositionInitializerEx that I linked to above? If you use that it will not hold any parts alive ever by default as it will only satisfy imports. If however you specify to AllowRecomposition, then the will be held alive.

Aug 10, 2010 at 9:06 PM

Hi Glen,

Yes, it was about the one you linked above. I'd be using the overload that sets AllowRecomposition to false which would in turn call SatisfyImportsOnce. So it seems that I don't have to call ReleaseExport even if the part implements IDisposable?

Thanks

Aug 10, 2010 at 9:25 PM

Two things:

- we actually hold conditional weak references

- ReleaseExport is applied transitively to a part graph, so it traverses the graph, in other words, it doesnt apply solely to the root part but also to its dependencies

 

Even when calling SatisfyImportsOnce, you may create parts that are non-shared and disposable. The guidance here is to call ReleaseExport or having child containers and call Dispose on those.

Aug 10, 2010 at 10:06 PM

Okay,

Sounds fair but now I have another question :-)

What's the recommended way to get the Export that ReleaseExport requires? In Windsor, I just give it the object that I want released but it seems that MEF want either an Export or a Lazy object. I've been looking at the API but can't seem to find a clear way to get an instance of the Export related to the object that I passed to SatisfyImportsOnce.

Could you point me in the right direction?

Thanks in advance 

Aug 10, 2010 at 10:12 PM

One of the overloads for SatisfyImports accepts a composable part. You can create a CP from an instance using the AttributedModelServices.CreatePart method. Once you have a part you "should" be able to get to an export to release it.

Glenn

Aug 10, 2010 at 10:47 PM

I'm hoping its just me missing something but looking at taking that route, I don't think that the ComposablePart will give me what I want. Say I did this:

AttributedModelServices.CreatePart(attributedPart));

This will return a ComposablePart but ComposablePart has two properties ExportDefinitions and ImportDefinitions which I'm assuming are the things that the attributedPart Export/Imports respectively. So it seems that it gives a description of the part. How do I get from a ComposablePart to an Export? I'm thinking I can't use the Import/ExportDefnitions of the part since those are the things the part uses/exposes and I want to release the part itself.

I've looked through the documentation section and I can't seem to find a code sample showing how to call ReleaseExport. I'm essentially trying to get to:

var x = ComponentInitializerEx.SatisfyImportsOnce(vm)
...
CompositionInitializerEx.Release(x);

I know that I may have to wrap some MEF stuff in ComponentInitializerEx to release the instance but the above code is my goal. If you could point me to a code sample that'd be great.

Thanks again.

 
Aug 10, 2010 at 11:46 PM
Edited Aug 10, 2010 at 11:47 PM

Humm...

I think I may be getting closer. I see that CompositionBatch has a RemovePart method.

Can I call container.Compose giving it a CompositionBatch in which I've called RemovedPart? Like

var batch = new CompositionBatch();
batch.RemovePart(AttributedModelServices.CreatePart(vm));
container.Compose(batch);

But now my question is does the ComposablePart that is passed to the RemovePart method of the batch have to be the same instance that was passed to the CreatePart method when the imports are being satisfied originally (i.e. when SatisfyImportsOnce is called)? And is my assumption correct that RemovePart will release the reference the container has to the object?

Aug 10, 2010 at 11:59 PM

Batch will only allow you to remove things that you added.

Aug 11, 2010 at 4:10 AM
I am not sure that will work because SatisfyImportsOnce never adds the part to the container.

Sent from my IPad.

On Aug 10, 2010, at 4:46 PM, "blackbelt" <notifications@codeplex.com> wrote:

From: blackbelt

Humm...

I think I may be getting closer. I see that CompositionBatch has a RemovePart method.

Can I call container.Compose giving it a CompositionBatch in which I've called RemovedPart? Like

var batch = new CompositionBatch();
batch.RemovePart(AttributedModelServices.CreatePart(vm));
container.Compose(batch);

But now my question is does the ComposablePart that is passed to the RemovePart method of the batch have to be the same instance that was passed to the CreatePart method when the imports are being satisfied originally (i.e. when SatisfyImportsOnce is called)?

Aug 11, 2010 at 5:26 PM

Doh! So if using RemovePart with a CompositionBatch and AttributedModelServices.CreatePart doesn't give me an ExportDefinition for the part for which SatisfyImportsOnce was called how do I go about telling the MEF to release the part?

Aug 11, 2010 at 7:06 PM

Here is what you do...don't use SatisfyImports. Instead create a ComposablePart using CreatePart, add that part to a batch and compose the batch. Then save that part reference and you can use it later to release / remove the part.

Glenn

Aug 12, 2010 at 8:25 PM

Thanks Glenn,

I do appreciate help specially since I believe you're not even on the MEF team anymore. I guess there is no REST for the wicked. :-)

 

Aug 12, 2010 at 9:11 PM

There's no REST from MEF :-)

Sep 9, 2010 at 1:56 PM

Hi Glen

Iam building a silverlight host which let's my company partners to create UI extensions to the  application.

There is a logic in the host that keeps mapping between a URL is mapped to an XML . The XML contains the names of the XAPS that holds the partner's implementation

In complex scenarios an XML can contain several XAP's so recomposition will occur.

I am using the deployment catalog service that you showed in the silverlight tv episode in order to remove / add XAPS

So each time a new URL is set in the browser the same process will occur :

- New page is set

- the page instance is transfered to the Composition Initializer.StaisfyImports

-Fetching the appropriate XML

-Reading the XAP names from the xml and use deployment catalog service to satisfy the imports

My problem is - MEF keeps the instance trasfered to satisfyimports of all the pages and call recompososition events on all the pages , whether this page is displayed in the browser or not.

 

I am using the ExportFactory in order to prevent MEF controling the life time of the extensiosn:

Something like

[ImportMany(AllowRecomposition=true)]

ExportFactory<{MyContract},{MyMetadata}> [] extensions{get;set;}

I tried to wrap the instance of page that is trasnfered to the staisfyImport with ExportFactory - No Luck

Please assist

Nir Tkuma

 

 

 

 

 

 

 

 

 

Oct 7, 2010 at 2:43 PM

Why doesn't MEF use WeakReferences when we do SatisfyImports ? My objects are only interested in recomposition as long as they're alive.

 It shouldn't be up to MEF to control the lifetime of the parts.