views:

348

answers:

4

How do you force a DataGridView to release its reference to a bound DataSet?

We have a rather large dataset being displayed in a DataGridView and noticed that resources were not being freed after the DataGridView was closed. If the user repeatedly views this report they eventually get an out of memory exception. ANTS Memory Profiler confirmed that the DGV is holding a reference despite dgv.DataSource being set to null.

+3  A: 

Do you have any events registered on the DataGridView like OnClick? Make sure you unregister all events, otherwise it will not be garbage collected

SwDevMan81
That isn't *quite* true; it is the `DataGridView` that can see **out** via its events - so if the `DataGridView` is being disposed its events will be nullified. A better question is: what can see the (old) `DataGridView`...
Marc Gravell
Yes, there are numerous events. I had read this elsewhere, but it didn't seem to be the cause.
Jerry Fernholz
+1  A: 

Are you closing down the entire Form? or just the DataGridView? I'm wondering if this is some caching in the BindingContext. You could try using a new binding-context per DataGridView?

Also; as always, double check events etc - in particular any using captured variables, as that is a subtle way of adding a dependency (note the capture scopes mean you might be capturing more than you think if you have complex anonymous methods / lambdas).

You might need to drop into profilers or windbg to find the remaining reference.

Marc Gravell
The data grid is on a form that gets closed each time. When the user runs new a report a new instance is created.
Jerry Fernholz
+1  A: 

The trick to forcing the DataGridView to release resources is to do the binding through the intermediary object BindingSource.

The code then looks something like this:

...
DataGridView dgvQueryResults;
DataTable m_dataTable;
BindingSource m_binder;

public void PopulateView()
{
  ...
  // Bind the data source through and intermediary BindingSource
  m_binder.DataSource = m_dataTable;
  dgvQueryResults.DataSource = m_binder;
  ...
}


/// <summary>
/// Frees lindering resources. Sets data bindings to null and forces 
/// garbage collection.
/// </summary>
private void ResetDataGridView()
{
  dgvQueryResults.DataSource = null;

  if (null != m_binder) m_binder.DataSource = null;
  m_binder = null;

  dataTable = null;

  // Force garbage collection since this thing is a resource hog!
  GC.Collect ();

  m_binder = new BindingSource ();
}

...
Jerry Fernholz
There are lots of good reasons to use the `BindingSource`, but this isn't one of them. If objects aren't being disposed because they're registered with events, using a `BindingSource` isn't addressing the problem at all. Also: there are seldom good reasons to call `GC.Collect()`, and this is not one of them.
Robert Rossney
Using BindingSource was suggested by Microsoft here: http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=92260Calling `GC.Collect()` is necessary in this case because the user may click through reports faster than the GC frees resources and the app will soon crash with an out of memory exception. Granted though, the reporting function could be rewritten to play a little nicer.BTW, this whole thing came about as a result of the classic not testing with a realistic data set 8]
Jerry Fernholz
A: 

You shouldn't set the DataGridView to null. You should call dispose on the DataGridView instead to allow it to properly clean itself up instead of adding more work to the GC to handle it.

Also, if you have any rooted references to the DataGridView, it will never be disposed (even if you call Dispose()). The GC thinks it's still alive. You should check for any rooted references to it - i.e. event handlers, static reference, etc. and remove those first before calling Dispose().

Dave Black