2

dotMemory tells me (screenshot below, "WPF binding leak") what there is a memory leak when binding to dictionary like this:

<ComboBox ItemsSource="{Binding Items, Mode=OneTime}"
          DisplayMemberPath="Value"
          SelectedValue="{Binding SelectedItem}"
          SelectedValuePath="Key" />

Question 1, to everyone: why is it a memory leak (namely what scenario should I use to run into problems) and how to fix it?


Queston 2, to dotMemory experts: why so basic mvvm application (see below) has so many problems reported? Should I fix those problems? How?


MCVE (create new WPF solution, use above code in xaml) code behind:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = new ViewModel();
    }
}

public class ViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    public void OnPropertyChanged([CallerMemberName] string property = "") =>
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));

    public Dictionary<string, string> Items { get; } = new Dictionary<string, string>
    {
        { "1", "One" },
        { "1a", "One and a" },
        { "2a", "Two and a" },
    };

    string _selectedItem = "1a";
    public string SelectedItem
    {
        get { return _selectedItem; }
        set
        {
            _selectedItem = value;
            OnPropertyChanged();
        }
    }
}
Sinatr
  • 20,892
  • 15
  • 90
  • 319
  • Side note, binding to dictionaries sucks, because bindings don't know jack about dictionaries, so they treat them as IEnumerable>. Because of this, you lose out on WPF features like automatic DataTemplate selection. Using a KeyedCollection that implements INotifyCollectionChanged is a better idea, as it implements IEnumerable. Also, `16b` isn't much of a worry :/ –  Jan 25 '18 at 18:21

2 Answers2

4

Binding target objects that do not implement the INotifyPropertyChanged interface or do not use the OneTime binding mode

Answer 1: Xaml is bound to Dictionary which is a collection of KeyValuePair and Value property of it is specified as a source for DisplayMemberPath. KeyValuePair which is exposed doesn't implement INotifyPropertyChanged interface and there is no way to specify OneTime binding mode for DisplayMemberPath. So, all items of Dictionary will stay in memory forever.

Answer 2: dotMemory reports potential problems, only you can determine if it is a real problem or not. Unfortunately .NET itself makes string duplicates and creates array which is never will be filled with data, dotMemory reports them too because can't distinguish if these objects created by "user" or by system. I would recommend you to see why do you have finalized objects, it seems that you forget to call IDisposable.Dispose method for some objects. And check if these not filled arrays created by you or not.

Ed Pavlov
  • 2,353
  • 2
  • 19
  • 25
  • 1. *"all items of Dictionary will stay in memory forever"* - do you mean until view is unloaded? I understand [mechanics](https://stackoverflow.com/a/7767274/1997232), my question was rather how to exploit it (aka, what to do wrong to have it explode) and when it's safe, or rather is there a safe way. – Sinatr Jan 25 '18 at 13:24
  • 2. I provided a *complete* code and my queston was rather why do I see those at all. And I put some irony by asking how can I fix those, because they are not mine. I can ignore those, but they are noisy and it will be hard to spot my own problems. E.g. how am I suppose to find my own "sparse arrays" if blank application has already 1000? – Sinatr Jan 25 '18 at 13:25
  • Forever of until view is unloaded in truth I don't remember, need to be checked. What to do wrong - bind to a property of type which does not implement `INotifyPropertyChanged` (with any binding mode except `OneTime`) If your "sparse array" will exceed 4Kb (in this case) you will see it :) To be serious we are going to add possibility to hide unwanted inspection results. No estimation still yet. – Ed Pavlov Jan 25 '18 at 14:39
1

The reason you are getting a Memory leak is that you are binding to an object that doesn't implement the interface INotifyPropertyChanged.

When we bind to an dictionaries' Value property...

the binding target starts listening for property change notifications. If the property is not a DependencyProperty or an object that implements INotifyPropertyChanged, WPF will resort to subscribing to the ValueChanged event of the System.ComponentModel.PropertyDescriptor class to get notifications when the source object’s property value changes.

Why is this a problem? Well, since the runtime creates a reference to this PropertyDescriptor, which in turn references our source object, and the runtime will never know when to deallocate that initial reference (unless explicitly told), both the PropertyDescriptor as well as our source object will remain in memory.

(source)

This is solved by binding to an ObservableDictionary<Key, Value>

ice1e0
  • 947
  • 7
  • 15
aydjay
  • 858
  • 11
  • 25
  • What is `ObservableDictionary`? And I still don't understand how to make memory leak. I've wpf application in production without memory leaks (it runs for days) with many bindings to dictionaries. – Sinatr Jan 04 '20 at 14:16