Binding WPF DataGrid ItemsSource creates memory le

2019-09-16 21:00发布

问题:

I'm simply creating a Observable collection in Background and binding to it.

All objects inherit from INotifyPropertyChanged. but nevertheless the Memory consumption is continuously raising.

The following Objects instances are continuously raising

WeakReference                                                                                       
FrugalObjectList<WeakEventManager+Listener>                                                         
ConditionalWeakTable<TKey, TValue>+Entry<Object, Object>[]                                        
WeakEventTable+EventKey                                                                             
ConditionalWeakTable<Object, Object>                                                              
SingleItemList<WeakEventManager+Listener>                                                           
Object                                                                                              
Int32[]                                                                                             
WeakEventManager+ListenerList<NotifyCollectionChangedEventArgs>                                     
WeakEventManager+ListenerList<PropertyChangedEventArgs>                                             
HybridDictionary                                                                                    
ListDictionary                                                                                      
ListDictionary+DictionaryNode                                                                       
WeakEventManager+ListenerList<EventArgs>                                                            
WeakEventManager+ListenerList<CurrentChangingEventArgs>                                             
CollectionRecord                                                                                    

I'm using .net 4.5.2.

See also the following Screenshots:

MemoryConsumptionOverview

ClassesIncreasing

Attached the sample code

XAML Markup:

<Window x:Class="BindingDataGridTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="120" Width="200"
        DataContext="{Binding RelativeSource={RelativeSource Self}}">
    <Grid>
        <DataGrid ItemsSource="{Binding BindingVals, Mode=OneWay}" />
    </Grid>
</Window>

Code behind:

using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows.Threading;

namespace BindingDataGridTest
{
    public partial class MainWindow : INotifyPropertyChanged
    {

        public ObservableCollection<Values> BindingVals { get; set; }

        public MainWindow()
        {
            BindingVals = new ObservableCollection<Values>();
            InitializeComponent();

            DispatcherTimer myTimer = new DispatcherTimer { Interval = new TimeSpan(20) };
            myTimer.Tick += CreateVals;
            myTimer.Start();

        }
        private void CreateVals(object sender, EventArgs e)
        {
            Values myMainval = new Values
            {
                MyVal = "1V" + new Random().Next()
            };

            BindingVals.Clear();
            BindingVals.Add(myMainval);

            GC.Collect();
        }

        public event PropertyChangedEventHandler PropertyChanged;
    }

    public class Values : INotifyPropertyChanged
    {
        public string MyVal { get; set; }

        public event PropertyChangedEventHandler PropertyChanged;

    }

}

回答1:

It's caused by DispatcherTimer.

A DispatcherTimer will keep an object alive whenever the object's methods are bound to the timer. https://msdn.microsoft.com/ru-ru/library/system.windows.threading.dispatchertimer(v=vs.110).aspx

Use System.Timers.Timer instead and Dispatcher separately.

    public MainWindow()
    {
        BindingVals = new ObservableCollection<Values>();
        InitializeComponent();

        System.Timers.Timer myTimer = new Timer {Interval = 20};
        myTimer.Elapsed += CreateVals;
        myTimer.Start();

    }

    private void CreateVals(object sender, EventArgs e)
    {
        Dispatcher.Invoke(() =>
        {
            Values myMainval = new Values
            {
                MyVal = "1V" + new Random().Next()
            };

            BindingVals.Clear();
            BindingVals.Add(myMainval);

            GC.Collect();
        });

    }