简体   繁体   中英

memory leaks in WPF MVVM Prism app

I have a WPF application that is written using MVVM Prism. It has a lot of tabs. Those tabs consume about 2..3 MB of memory each. The client complains that after opening and closing few dozens of tabs the application consumes much more memory than it did from the start. Also, opening new tabs requires more memory, so application doesn't use old tabs and creates new ones instead.

So obviously old tabs are not garbage-collected. Obviously because there are some links pointing to them.

How do I garbage-collect them any way? Should I just implement IDisposable, and remove all references to whatever it is possible? And make sure that Dispose method is also called in destructor? I am not sure that I can remove all unrequired references.

Maybe there is a good tool I can use that might aid me with this problem?

This is obviously a loaded question. The memory consumption might not only be caused by memory leaks! Every app is different, and there is no silver bullet, so here some ideas that may help.

  1. get the ANTS profiler, it has 2 weeks trial period + great tutorials. It'll point you to lots of things, like zomby objects, etc.

  2. WPF is a hog, so the more tabs you open (controls that don't go away) the more memory it will take. Look through your XAML, can you trim it down. For example, use TextBlock instead of labels. Remove extra nested controls, like StackPanel within a StackPanel, or Grid within a Grid, within a stackpanel - put all those in one grid, and make use of row/columns.. If you have an item template where each item does something that makes it complex, try to change that. For example, if on focus it draws a border around the item and does something fancy, ask a question, can I remove that from each item, and create one control that calculates its location and places itself aproprietly.

  3. Do you have the Views (usercontrols, or controls) per tab that are the same type, just a different instance? If so can you recycle them? I was on a project a year+ ago where we had some menu creating a tab for some criteria, and then another, and another. Criteria was different, but the view type was the same. It was just being injected with different info - Prism was creating a new View control for each one of these tabs, which is obviously expensive. What we ended up doing is creating different ViewModels, but keeping the same instance (recycling by removing/adding back when needed to the region) of that expensive view. To do so each ViewModel would on navigation (sample)

     //detaching from Prism region allows for recycling public override void OnNavigatedFrom (NavigationContext navigationContext) { var view = _container.Resolve (typeof (Object), "NameOfTheView"); if (view != null) navigationContext.NavigationService.Region.Remove (view); } //similarly you can readd it where your think it is nedded.. public override void OnNavigatedTo (NavigationContext navigationContext) { base.OnNavigatedTo (navigationContext); RestoreDataState (_state); 

    }

    this link can be useful: http://blogs.msdn.com/b/dphill/archive/2011/01/23/closable-tabbed-views-in-prism.aspx

  4. Note on Garbage Collection: The whole purpose of the .NET garbage collector is to manage memory on our behalf. However, in some very rare circumstances, it may be beneficial to programmatically force a garbage collection using GC.Collect().

    Specifically:

    a.When your application is about to enter into a block of code that you don't want interrupted by a possible garbage collection.

    b.When your application has just finished allocating an extremely large number of objects and you wish to remove as much of the acquired memory as soon as possible. (this was the case in my project)

    c. also: http://blogs.msdn.com/b/ricom/archive/2004/11/29/271829.aspx

I would definitely implement Dispose methods on every ViewModel, where I would set everything big to null, don't forget to unsubscribe from events, and things like timers. You could call GC.Collect (); but read the note above on that.. Don't forget to clean up complex object, don't just set them to null. For example, in my Dispose we had something like:

ClearDisplayGrid ();

which in turn was doing this:

private void ClearDisplayGrid ()
    {
        foreach (var r in DisplayGrid.MyItems.SelectMany (it => it.SubItems))
        {
            r.IsSelectedChanged -= ReadingIsSelectedChanged;
            r.InEditChanged -= ReadingInEditChanged;
            r.PropertyChanged -= ReadingPropertyChanged;
        }
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM