简体   繁体   中英

.NET data bindings and memory leak

I have a memory leak in my application that I cannot find the real source of. There is this line of code in a control called DataTableEditor :

refreshButton.DataBindings.Add("Enabled", asyncSqlResultsViewer, "CanRefresh", true, DataSourceUpdateMode.Never);

The asyncSqlResultsVewer is just a user control with a DataGridView that shows data. Used to load the data asynchronously but it doesn't anymore. The DataTableEditor control is another user control that contains an AsyncSqlResultsVewer . The leak seems related to this because it shows a PropertyDescriptor as part of the path that is holding the object alive:

.net内存分析器屏幕截图

I'm a newbie using the .NET Memory Profiler but the thing is, removing the line above gets rid of the leak. Removing the bound with DataBindings.Remove also fixes the leak. But, is this supposed to happen? The asyncSqlResultsVewer is disposed and as far as I know de-referenced together with the DataTableEditor , so every reference among them shouldn't keep them alive anymore as long as no one else references any of them (right?). I can still remove the bound just in case or as a hack, but I'd like someone to help me figure out what the real cause of the leak is.

In case you're in doubt this is an actual memory leak, the application crashes after opening and closing several DataTableEditor s that show a large amount of data. Also, the profiler is telling me the object has been disposed but there are still references to it.

If there is any other piece of information that might help you help me figure this out please let me know.

Edit: I think I know where the leak may come from. refreshButton above is an instance of this class:

public class ToolStripBindableButton : ToolStripButton, IBindableComponent, INotifyPropertyChanged
{
    private ControlBindingsCollection dataBindings;

    private BindingContext bindingContext;

    public ControlBindingsCollection DataBindings
    {
        get
        {
            if (dataBindings == null) dataBindings = new ControlBindingsCollection(this);
            return dataBindings;
        }
    }

    public BindingContext BindingContext
    {
        get
        {
            if (bindingContext == null) bindingContext = new BindingContext();
            return bindingContext;
        }
        set { bindingContext = value; }
    }

    public override Image Image
    {
        get { return base.Image; }
        set { SetImage(value); }
    }

    private void SetImage(Image value)
    {
        if (base.Image != value)
        {
            base.Image = value;
            OnPropertyChanged(new PropertyChangedEventArgs("Image"));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        if (PropertyChanged != null) PropertyChanged(this, e);
    }
}

Adding a Dispose override and doing dataBindings.Clear() fixes the leak. Is this something I'm supposed to do or is the real cause of the leak still elsewhere?

I belive that you already answered the memoryleak problem yourself.

In my opinion it seems likely that cource of the leak is the refreshbutton (instance of ToolStripBindableButton ).

When the ToolStripButton.Dispose() is called it knows how to cleanup/release all of its objects that requirs so. However it have no way of knowing how you extented the class, and therefore it cannot help you clean up.

I would have expected ControlBindingsCollection and BindingContext to inherit IDisposable thereby making it clear that cleanup was needed.

protected override void Dispose(bool disposing)
{
  base.Dispose(disposing);

  if (disposing && dataBindings != null)
  {
    dataBindings.Clear();
  }
  dataBindings = null;
  dindingContext = null;
  PropertyChanged = null;
}

It's hard to tell wheather you found the souce of the problem, but I would keep an eye on any events not beeing disposed/released/killed-somehow.

Also keep a close look at your components on your forms. Theise only gets disposed automatic, if the are created with an instance of IContainer, and the WinForms designer can choose not to do so.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM