Posted by: beatkiener | January 28, 2010

Memory leak with focusable UIElement in Silverlight?

Suppose we have a StackPanel with following controls inside.

        <StackPanel x:Name="stackpanel" Width="200">
            <TextBox />
            <CheckBox />
            <ComboBox />
            <controls:DatePicker />
            <Button />
            <local:MyTextBox />
        </StackPanel>

 

All of them are focusable controls. MyTextBox is a subclass of TextBox as following:

    public class MyTextBox : TextBox
    {
        ~MyTextBox()
        {
            Debug.WriteLine("MyTextBox.Finalize");
        }
    }

 

Now, if I remove the Children in the stackpanel…

this.stackpanel.Children.Clear();


…all Children gets garbage collected. Everything correct until here.

 

Restart the application. Now set the focus to any element in the stackpanel and stepwise to all others too.

If I now remove all children from the stackpanel none of them gets garbage collected.

this.stackpanel.Children.Clear();


Unfortunately, this problem has several negative side effects. We are using the MVVM pattern and when we databind the controls to a ViewModel then the ViewModel is also part of the memory leak, because the binding contains a reference to the ViewModel.

It also doesn’t help to remove the whole page. I’ve tested the code within a navigation application with the same result.

 

Demo Project

Donwload the demo project here.

Please note: perhaps you have to run the GC n-times to collect all object, but at the end all elements which had the focus at any time will never be collected neither when you run the GC 1000 times or neither when you release the whole user control.

 

XAML snapshot

    <Grid x:Name="LayoutRoot" Background="White">

        <StackPanel >
            <Button x:Name="btnRemove" Content="Remove all" Click="btnRemove_Click" />
            <Button x:Name="btnCollect" Content="Run Garbage Collector" Click="btnCollect_Click"  />
        </StackPanel>

        <StackPanel x:Name="stackpanel" Width="200">
            <TextBox />
            <CheckBox />
            <ComboBox />
            <controls:DatePicker />
            <Button />
            <local:MyTextBox />
        </StackPanel>

    </Grid>

 

C# snapshot

    public partial class MainPage : UserControl
    {
        private List<WeakReference> weakRefs = new List<WeakReference>();

        public MainPage()
        {
            InitializeComponent();

            // collect all children to get a weakreference to it
            foreach (UIElement element in this.stackpanel.Children)
                weakRefs.Add(new WeakReference(element));

            // add the view model to the list too
            weakRefs.Add(new WeakReference(this.DataContext));
        }

        private void btnRemove_Click(object sender, RoutedEventArgs e)
        {
            // removing the DataContext forces a rebind of all databound controls. 
            // This releases the binding-reference from the control to the ViewModel.
            this.DataContext = null;

            // important: first remove the datacontext and then clear the children 
            //            otherwise the ViewModel is part of the memory leak.
            this.stackpanel.Children.Clear();
        }

        private void btnCollect_Click(object sender, RoutedEventArgs e)
        {
            GC.Collect();
            GC.WaitForPendingFinalizers();

            // count how many reference are not collected
            var objectsAlive = (from wr in weakRefs
                                where wr.IsAlive
                                select wr.Target).ToList();

            Debug.WriteLine("Total instances not collected: " + objectsAlive.Count);
            objectsAlive.ForEach(obj => Debug.WriteLine("     " + obj.GetType().Name));
        }

    }

    // class to output the finalizing
    public class MyTextBox : TextBox
    {
        ~MyTextBox()
        {
            Debug.WriteLine("MyTextBox.Finalize");
        }
    }

 

Feedback

I’ve posted this problem on the silverlight.net forum as a possible bug. Check it out here


Responses

  1. I’ve noticed that my machine has a memory leak with WPF too. After a control got the focus then the control will never being collected. Well, the last few hours I’ve reinstalled my machine completely (anyway I had to update from Win7-beta to Win7 the next days). Now the problem does not occur anymore. But what was the reason for this memory behavior? I did a quick system comparing (I’ve installed the Win7 onto a new disk). One difference I found is that I had installed several screen readers on the old system to test the Silverlight accessibility support. I can’t finally say this caused the problem but I see a possible reason for it.

    Well, the problem does not exist anymore, but one question is still open for me: could the same happen on a customer machine?

    Currently I can’t investigate more time into this.


Leave a response

Your response:

Categories