DataGridView.DataSource may eat your memory

It is suboptimal coding practice to repeatedly assign a new value to the DataSource property of a System.Windows.Forms.DataGridView.

 

                    // Get jobs from service.

                    List<JobInfo> activeJobs = m_worker.GetJobs(JobListType.ActiveJobs);

 

                    // Update controls.

                    Invoke(new MethodInvoker(delegate()

                    {

                        activeJobsDataGridView.DataSource = activeJobs;

                    }));

 

Similarly, assigning different values of DataMember is also bad, but less likely to be used since it doesn’t make as much sense.

It is suboptimal because it exposes an issue with the DataGridView’s BindingContext, which will usually be the containing form’s BindingContext.

The BindingContext class has no public members that allow removing of a data source. So, on each assignment to DataSource, DataGridView can do nothing but happily add a new item to the BindingContext to get the new BindingManagerBase, careless about removing the old one.

The default implementation of BindingContext will create a BindingContext+HashKey and some WeakReference objects to get a hold of the DataSource and the BindingManagerBase created and add them to the internal listManagers Hashtable.

When assigning a new DataSource, the old DataSource and BindingManagerBase objects will be garbage-collected sooner or later. The BindingContext+HashKey and WeakReference objects however will not and the BindingContext’s listManagers Hashtable will only grow (until the containing form is finally closed).

If your form stays around for a while (visible or not), eventually your managed heap may contain things like these (windbg -p <pid>, .loadby sos mscorwks, !dumpheap -stat):

7b4bb050  6250113    125002260 System.Windows.Forms.BindingContext+HashKey
791242ec      440    172866960 System.Collections.Hashtable+bucket[]
79108e38 12500371    200005936 System.WeakReference

This is a whopping chunk of almost 500 mega-bytes just to hold on to a couple of objects that will mostly be long gone anyway.

One solution is to not have your forms hang around that long. Service processes should not have a long-living user interfaces anyway.

The other solution is to not re-assign the DataSource. Create one, assign one, stick with it.

                    // Get jobs from service.                   

                    activeJobs.Clear();

                    activeJobs.AddRange(m_worker.GetJobs(JobListType.ActiveJobs));

 

                    // Update controls.

                    Invoke(new MethodInvoker(delegate()

                    {

                        activeJobsDataGridView.ResetBindings();

                    }));

 

This goes to show that WeakReference objects are not cheap and can not be left unattended, especially not for "smart" memory management.

Another option might be to use a custom implementation of BindingContext that exposes the protected Remove and Clear methods or at least uses them following some schedule.

Advertisements
This entry was posted in Coding Horror. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s