Привязки данных .NET и утечка памяти

У меня есть утечка памяти в моем приложении, настоящий источник которой я не могу найти. В элементе управления с именем DataTableEditor есть эта строка кода:

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

asyncSqlResultsVewer — это просто пользовательский элемент управления с DataGridView, который показывает данные. Используется для асинхронной загрузки данных, но это больше не так. Элемент управления DataTableEditor — это еще один пользовательский элемент управления, содержащий элемент AsyncSqlResultsVewer. Утечка кажется связанной с этим, потому что она показывает PropertyDescriptor как часть пути, который удерживает объект в живых:

скриншот профилировщика памяти .net

Я новичок в использовании .NET Memory Profiler, но дело в том, что удаление строки выше устраняет утечку. Удаление привязки с помощью DataBindings.Remove также устраняет утечку. Но должно ли это случиться? asyncSqlResultsVewer удалены и, насколько я знаю, разыменованы вместе с DataTableEditor, поэтому каждая ссылка среди них не должна поддерживать их жизнь, пока никто другой не ссылается ни на один из них (правильно?). Я все еще могу удалить привязку на всякий случай или в качестве хака, но я хотел бы, чтобы кто-нибудь помог мне понять, какова настоящая причина утечки.

Если вы сомневаетесь, что это настоящая утечка памяти, приложение вылетает после открытия и закрытия нескольких DataTableEditor, которые показывают большой объем данных. Кроме того, профилировщик сообщает мне, что объект был удален, но ссылки на него все еще есть.

Если есть какая-либо другая информация, которая может помочь мне разобраться в этом, пожалуйста, дайте мне знать.

Редактировать: кажется, я знаю, откуда может произойти утечка. refreshButton выше является экземпляром этого класса:

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);
    }
}

Добавление переопределения Dispose и выполнение dataBindings.Clear() устраняет утечку. Это то, что я должен сделать, или настоящая причина утечки находится где-то еще?


person Juan    schedule 13.02.2013    source источник
comment
Довольно сложно дать эффективный ответ, если вы не можете показать какой-либо относительный код. У вас есть доступ к исходному коду, о котором идет речь?   -  person MethodMan    schedule 14.02.2013
comment
Я знаю, но не уверен, что показать, слишком много фрагментов кода, которые могут быть связаны с этим.   -  person Juan    schedule 14.02.2013
comment
Является ли кнопка refreshButton, которую вы добавляете привязкой, также выходящей за рамки и удаленной? Мне интересно, является ли это результатом самой привязки, поскольку с ней связаны события, которые будут иметь ссылку на DataGridView.   -  person Jay    schedule 14.02.2013


Ответы (1)


Я считаю, что вы уже сами ответили на проблему утечки памяти.

По моему мнению, источником утечки является кнопка обновления (экземпляр ToolStripBindableButton).

Когда вызывается ToolStripButton.Dispose(), он знает, как очистить/освободить все свои объекты, которые для этого требуются. Однако у него нет возможности узнать, как вы расширили класс, и поэтому он не может помочь вам очистить.

Я ожидал, что ControlBindingsCollection и BindingContext наследуют IDisposable, тем самым давая понять, что необходима очистка.

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

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

Трудно сказать, нашли ли вы источник проблемы, но я бы следил за любыми событиями, которые не были удалены/освобождены/убиты каким-то образом.

Также внимательно следите за своими компонентами в формах. Они удаляются автоматически только в том случае, если они созданы с экземпляром IContainer, и дизайнер WinForms может не делать этого.

person Jens Kloster    schedule 14.02.2013