Home Dashboard Directory Help
Search

WPF TreeView memory leak (.NET 4.5) by omjbe


Status: 

Active


9
0
Sign in
to vote
Type: Bug
ID: 778557
Opened: 2/5/2013 4:38:14 AM
Access Restriction: Public
1
Workaround(s)
view
5
User(s) can reproduce this bug

Description

The WPF TreeView holds the data bound object of the selected TreeViewItem in the memory. When the ItemsSource is cleared then this object should be freed as well but this does not happen.

The "Steps to reproduce" explain how to reproduce this memory leak.

The attachment "WpfTreeViewMemoryLeak.pdf" shows a memory analysis done with the Ants Memory Profiler. It shows that the HierarchicalModel is kept by the TreeView in memory.

Note: The memory leak does not occur when never a TreeViewItem is selected. Skip step 5. to see this.
Details
Sign in to post a comment.
Posted by omjbe on 6/10/2014 at 10:57 PM
Please do not close bugs without even providing a workaround. I have not found a “good” workaround so far. I believe this memory issue can only be fixed in WPF itself.
Posted by omjbe on 6/9/2014 at 11:12 PM
I do not understand. This bug affects all WPF developers that are using the TreeView together with Binding. I believe this should be a very high number of affected WPF developers.
Posted by Microsoft on 6/6/2014 at 7:06 PM
The WPF team has recently reviewed this issue and will not be addressing this issue as at this time the team is focusing on the bugs impacting the highest number of WPF developers. If you believe that this was resolved in error, please reactivate this bug with any necessary supporting details.

We appreciate the feedback. However, this issue will not be addressed in the next version of WPF. Thank you.
–WPF Team.
Posted by omjbe on 3/6/2014 at 8:50 AM
This bug is in the .NET Framework Version 4.5.1 as well. Please fix this bug in the next version. This is VERY important for us. Thanks.
Posted by Microsoft on 3/5/2013 at 1:14 PM
We agree that this is not a good bug to have in the product; however we will not be fixing it at this point. Sorry for the inconvenience.
Posted by CliffCawley on 2/24/2013 at 5:06 PM
I only just discovered this bug in our environment too. It's definitely a problem for us. The only way for us to ensure it doesn't happen is to subclass the TreeView and force clearing all references on selection change.
Posted by omjbe on 2/21/2013 at 2:52 AM
This is a real serious bug (Our application needs with every document 100MB more memory because of this!). And every developer using the WPF TreeView might run into this memory leak issue.

It can only be fixed in the .NET Framework. I do not understand how such a severe bug can be closed without any fixing.
Posted by Microsoft on 2/20/2013 at 1:09 PM
The WPF team has recently reviewed this issue and will not be addressing this issue as at this time the team is focusing on the bugs impacting the highest number of WPF developers. If you believe that this was resolved in error, please reactivate this bug with any necessary supporting details.

We appreciate the feedback. However, this issue will not be addressed in the next version of WPF. Thank you.
–WPF Team.
Posted by omjbe on 2/10/2013 at 9:56 PM
The finalizer is only called when the garbage collector collects the object. Therefore, the "Steps to reproduce" set a break point into the finalizer and in the last step the garbage collector is forced to collect all "free" objects.

The sample creates three "HierarchicalModel" instances. After the last step the break point should hit three times for every instance. But this does not happen because of a memory leak in the TreeView.
Posted by Microsoft on 2/7/2013 at 3:26 AM
Thanks for your feedback.

We are rerouting this issue to the appropriate group within the Visual Studio Product Team for triage and resolution. These specialized experts will follow-up with your issue.
Posted by Microsoft on 2/6/2013 at 12:55 AM
Hi omjbe, my i ask you how to check whether the "Item A" and "Sub Item A1" are still im memory?
Posted by Microsoft on 2/5/2013 at 4:50 AM
Thank you for your feedback, we are currently reviewing the issue you have submitted. If this issue is urgent, please contact support directly(http://support.microsoft.com)
Sign in to post a workaround.
Posted by AbishaiSharper on 5/2/2013 at 3:07 PM
This is a class I implemented to clear out the EffectiveValueEntries collection in which the reference was maintained. It is a little heavy-handed, but it does the trick. It is derived from a solution found at http://stackoverflow.com/questions/12722813/wpf-treeview-leaking-the-selected-item

/// <summary>A tree view which will release internally stored selected item remnants upon the selection changing when items or items source is empty.</summary>
/// <remarks>
/// WPF TreeViews have a memory leak which have to do with selected items. In certain situations, when a selected item is cleared, the TreeView will still have a reference to it in an internal EffectiveValueEntries[].
/// </remarks>
public class ManagedTreeView : TreeView
{
    private static readonly MethodInfo _effectiveValuesGetMethod;
    private static readonly MethodInfo _effectiveValueEntryValueGetMethod;

    static ManagedTreeView()
    {
        var effectiveValuesPropertyInfo = typeof(DependencyObject).GetProperty("EffectiveValues", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.DeclaredOnly | System.Reflection.BindingFlags.Instance);
        _effectiveValuesGetMethod = effectiveValuesPropertyInfo.GetGetMethod(nonPublic: true);

        var effectiveValueEntryType = AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes()).First(t => t.Name == "EffectiveValueEntry");
        var effectiveValueEntryValuePropertyInfo = effectiveValueEntryType.GetProperty("Value", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.DeclaredOnly | System.Reflection.BindingFlags.Instance);
        _effectiveValueEntryValueGetMethod = effectiveValueEntryValuePropertyInfo.GetGetMethod(nonPublic: true);
    }

    //used to store multiple types which may be present in the treeview hierarchy
    private readonly HashSet<Type> _typesSelected = new HashSet<Type>();

    protected override void OnSelectedItemChanged(RoutedPropertyChangedEventArgs<object> e)
    {
        base.OnSelectedItemChanged(e);

        if (e.NewValue != null)
        {
            _typesSelected.Add(e.NewValue.GetType());
        }

        if (Items.Count == 0 && _typesSelected.Count > 0)
        {
            //EffectiveValueEntries[]
            var effectiveValues = (Array)_effectiveValuesGetMethod.Invoke(this, null);

            for (int i = 0; i < effectiveValues.Length; i++)
            {
                //the value of the EffectiveValueEntry at the index
                var value = _effectiveValueEntryValueGetMethod.Invoke(effectiveValues.GetValue(i), null);

                if (value != null)
                {
                    if (_typesSelected.Contains(value.GetType()))
                    {
                        /*
                         * Clear out any direct references to types in the tree
                         */
                        effectiveValues.SetValue(null, i);
                    }
                    else
                    {
                        var bindingExpression = value as BindingExpression;
                        if (bindingExpression != null)
                        {
                            /*
                             * Clear out bindings which reference objects types used in the tree
                             */
                            if (bindingExpression.ParentBinding.Source != null && _typesSelected.Contains(bindingExpression.ParentBinding.Source.GetType()))
                            {
                                effectiveValues.SetValue(null, i);
                            }
                        }
                    }
                }
            }

            _typesSelected.Clear();
        }
    }
}
File Name Submitted By Submitted On File Size  
WpfTreeViewMemoryLeak.pdf 2/5/2013 19 KB
WpfTreeViewMemoryLeak.zip 2/5/2013 11 KB