Remove MEF Region from Memory

Topics: Prism v4 - WPF 4
Feb 11, 2011 at 3:40 AM
Edited Feb 12, 2011 at 9:24 PM

Edit: This was resolved below, it had nothing to do with regions or navigation, it turns out that even if you mark your export as NonShared Mef will hold a reference to it if it implements IDisposable or it AllowsRecomposition, see below.

I'm fighting with a Memory Leak and I'm not sure how to resolve it.

A rough outline of my app is:

ShellWindow
--ContentControl MainRegion
----UserControl1
------ContentControl L1Region
--------UserControl2

When I navigate it might look like this:

ShellWindow
--ContentControl MainRegion
----UserControl3
------ContentControl L1Region
--------UserControl2

Before I add UserControl3 I remove all views from the MainRegion and the L1Region and then I remove the L1Region from the region manager.

UserControl2s ViewModel never get's disposed.  And based on what I saw while I was debugging L1Region is never being picked up by the garbage collector.

I've reviewed my code carefully and removed anything I thought might have a remote chance of causing this.  So far no luck.  A repro app is going to take a bit to write, so I thought I'd see if anyone had any ideas for me while I work on that.

Feb 11, 2011 at 1:16 PM

When you remove a view from a region, it will be disposed as well as its objects (e.g. a View Model) when the garbage collection occurs. Take into account that the garbage collector will dispose all these components successfully if you don’t have any pointer alive to your objects from other place.

Therefore, your view models should be disposed when your views are disposed from memory.

On the other hand a memory leak has been detected and you might find this thread handy where this kind of issue is covered.

Please let me know if this information helps you.

Thanks,

Miguel Bronzovic
http://blogs.southworks.net/mbronzovic

 

Feb 11, 2011 at 4:57 PM

That blog post does look like my issue, but just adding his code didn't resolve it for me. 

I'm working in an app to reproduce the issue.  And knowing about that possibility will be a definite help, so thanks!!!

I'll let you know what turns up in the repro app.  Probably just some silly error on my part, I'm still learning prism.

Feb 11, 2011 at 7:24 PM
JohnFenton wrote:

That blog post does look like my issue, but just adding his code didn't resolve it for me. 

I'm working in an app to reproduce the issue.  And knowing about that possibility will be a definite help, so thanks!!!

I'll let you know what turns up in the repro app.  Probably just some silly error on my part, I'm still learning prism.


The ANTS Memeory Profiler was instrumental in helping me locate our memory leak.   I was able to resolve it during the trail period but found it so useful we ended up purchasing it.   You basically take a snapshot, run part of the application, exit it, and take another snapshot.   It provides a great deal of information (to include whether it wasn't GC'd because of event subscriptions).

 http://www.red-gate.com/products/dotnet-development/ants-memory-profiler/ has a 14 day free trial.

Feb 11, 2011 at 8:35 PM
Edited Feb 11, 2011 at 8:39 PM

It was easier to reproduce than I thought!

This (which I'm guessing is the wrong way to do it) is how I am removing the views:

        List<object> views = new List<object>();
        foreach (var item in region.Views)
          {
          views.Add(item);
          }
        foreach (var item in views)
          {
          region.Remove(item);
          }

It is not doing it, even when there are no child views on the views I am removing.

Note, I am not implementing KeepAlive as that did not release the views quickly enough for me to be able to add another view with the same region names in it.  Instead I am trying to manually remove and release the views/regions.

@Billkrat Ants is great!  But, my trial period is up and it's not in the budget at the moment :-(

I can put up a simplified repro app sometime tonight or tomorrow.  But that code appears to be the source of my troubles, it's not doing what I think it should do.  The view models that are attached to those views that I am removing do not dispose till the application exits. (I am forcing the GC to run)

        List<object> views = new List<object>();
        foreach (var item in region.Views)
          {
          views.Add(item);
          }
        foreach (var item in views)
          {
          region.Remove(item);
          }

This is t

 

Feb 12, 2011 at 4:30 AM
Edited Feb 12, 2011 at 6:12 PM

OK, here is the app that reproduces this issue:

(See below, no longer needed, so it has been deleted)

I pared it down to almost nothing.  Play a few times with the buttons, watch the debug Output window, you'll see it requesting the new view and removing the old view.  But you won't see any disposed messages.  Hit X on the window and you will see all the VeiwModels now get disposed.

I would greatly appreciated if some one else could run this and confirm that you can reproduce the error.

Note: I link to the prism DLLs directly so you may need to fix the dlls in the References for this to run.

Feb 12, 2011 at 4:40 PM

It appears to be something in how I am creating the views.  I removed the region.Add and they still persist. 

Feb 12, 2011 at 6:08 PM
Edited Feb 12, 2011 at 6:10 PM

LOL & Dang, tripped up by my own testing procedures.

From MEF & Lifetime:

The CompositionContainer will not hold references to parts it creates, unless for the following cases:

1. The ComposablePart.RequiresDisposal returns true – which for our default programming will be true only when it wraps an object instance that implements IDisposable

2. The part has one or more imports marked with AllowRecomposition=true. In this case, though, we hold conditional references, which means that while there’s an alive export instance the GC won’t collect the part instance.

3. Shared parts

Rather ironic, but they were not disposing because they implemented IDisposable!

I removed the IDisposable, and switched the test to storing weak references and checking IsAlive.  The objects are now getting GCed.

However, this does open a rather interesting question.

Prisms ServiceLocator does not Implement the Mef ReleaseExport, is there a way to do that in Prism?