views:

406

answers:

2

Hi

I have a TabControl in WPF / MVVM that is bound to an ItemsSource on top of an ObservableCollection. Each tab has its own collections and bindings with components such as Images, Richtextboxes, and user input boxes, and everything seems to work well.

However, I have noticed that each time I switch a tab, it uses about 100k of system memory which never gets reclaimed! If I hold ctrl-tab down to cycle through all tabs, I can use up 200 megs of memory within a minute.

Now - I created a blank WPF app with just a tab control, and it also uses memory for each tab switch (albiet MUCH less). Is this just some .NET bug, or a feature? Maybe it stores a breadcrumb trail, maybe its used for debugging (although I compiled in release mode).

How do I reclaim my memory? Or better yet, not lose memory to tab switchings?

+1  A: 

You might want to check if there are any event handlers left behind.

In the case where you registed an event in other control, the garbadge collector will not collect the objects that are no longer needed, because the event is still attached so to speak.

So if you registered Loaded somewhere in code behind

public ParentEditor()
{
    InitializeComponents();
    control.Loaded += OnControlLoaded;
}

or in XAML or the ParentControl

<Control Loaded="OnControlLoaded" />

You basicly have two solutions to tackle this problem:

Solution 1 - Remove event handlers when they are no longer needed:

You might want to remove this handler in the unloaded of the parent control like so:

public ParentEditor()
{
    InitializeComponents();
    control.Loaded += OnControlLoaded;

    this.Unloaded += OnParentUnloaded;
}

void OnParentUnloaded(object sender, RoutedEventArgs e)
{
    //Remove unloaded event
    this.Unloaded -= OnParentUnloaded;

    //Remove event from child control
    control.Loaded -= OnControlLoaded;        
}

You can also use the Unloaded event of the child control of course.. that is up to you..

Solution 2 - Using the WeakEvent pattern:

Another solution for events would be the WeakEvent pattern, which bypasses this problem.

Why Implement the WeakEvent Pattern?

Listening for events can lead to memory leaks. The typical technique for listening to an event is to use the language-specific syntax that attaches a handler to an event on a source. For instance, in C#, that syntax is: source.SomeEvent += new SomeEventHandler(MyEventHandler).

This technique creates a strong reference from the event source to the event listener. Ordinarily, attaching an event handler for a listener causes the listener to have an object lifetime that influenced by the object lifetime for the source (unless the event handler is explicitly removed). But in certain circumstances you might want the object lifetime of the listener to be controlled only by other factors, such as whether it currently belongs to the visual tree of the application, and not by the lifetime of the source. Whenever the source object lifetime extends beyond the object lifetime of the listener, the normal event pattern leads to a memory leak: the listener is kept alive longer than intended.

In either case, good luck.. its quite hard to find leaks like the one you are experiencing!

Arcturus
Thanks for the advice - very useful! But with MVVM, event handlers are not even used. The viewmodel knows nothing about the gui, and only exposes commands for the gui to hook into.
bluebit
A: 

Good advice, but in the end I decided a bindable textblock was more useful and simpler than a richtextbox. I never found out what was causing the leak - but it was definitely in the bindablerichtextbox code (OnInitialized was being called each time a tab switched, which was beyond my control).

The leaks are gone, and my app runs quicker because of the use of the simpler bindable textblock.

bluebit