Подтвердить что ты не робот

Циркулярные ссылки Причина утечки памяти?

Я пытаюсь запустить утечку памяти в приложении форм Windows. Я сейчас смотрю на форму, содержащую несколько встроенных форм. Меня беспокоит то, что ребенок формирует в своем конструкторе ссылку на родительскую форму и сохраняет ее в поле частного члена. Поэтому мне кажется, что пришло время сбора мусора:

Родитель имеет ссылку на дочернюю форму через коллекцию элементов управления (там встроена дочерняя форма). Ребенок не является GC'd.

Ребенок имеет ссылку на родительскую форму через поле частного члена. Родительская форма не GC'd.

Это точное понимание того, как сборщик мусора будет оценивать ситуацию? Любой способ "доказать" его в целях тестирования?

4b9b3361

Ответ 1

Отличный вопрос!

Нет. Обе формы будут (могут быть) GC'd, потому что GC не ищет ссылки в других ссылках напрямую. Он ищет только то, что называется ссылкой "Root"... Это включает в себя ссылочные переменные в стеке (переменная находится в стеке, фактический объект, конечно, находится в куче), ссылается на переменные в регистры процессора и ссылочные переменные, которые статические поля в классах...

Все остальные ссылочные переменные доступны только (и GC'd), если они ссылаются на свойство одного из "корневых" ссылочных объектов, найденных вышеуказанным процессом... (или в объекте, на который ссылается ссылка в корневой объект и т.д.)

Итак, только если одна из форм упоминается где-то еще в "корневой" ссылке - тогда обе формы будут в безопасности от GC.

Только так я могу придумать, чтобы "доказать" его (без использования утилит трассировки памяти) было бы создать пару сотен тысяч этих форм, в цикле внутри метода, затем, в то время как в методе, посмотрите на памяти приложения, затем выйти из метода, вызвать GC и снова посмотреть на след.

Ответ 2

Как уже говорили другие, GC не имеет проблем с циркулярными ссылками. Я просто хотел бы добавить, что обычным местом утечки памяти в .NET являются обработчики событий. Если одна из ваших форм имеет прикрепленный обработчик событий к другому объекту, который является "живым", тогда есть ссылка на вашу форму, и форма не получит GC'd.

Ответ 3

Работа по сборке мусора, отслеживая корни приложений. Корни приложений - это места хранения, содержащие ссылки на объекты в управляемой куче (или на нуль). В .NET корни

  • Ссылки на глобальные объекты
  • Ссылки на статические объекты
  • Ссылки на статические поля
  • Ссылки на стек для локальных объектов
  • Ссылки на стек для параметров объекта, переданных методам
  • Ссылки на объекты, ожидающие окончательной доработки
  • Ссылки в регистры процессора на объекты в управляемой куче

Список активных корней поддерживается CLR. Сборщик мусора работает, просматривая объекты в управляемой куче и видя, какие приложения по-прежнему доступны, т.е. Доступны через корень приложения. Такой объект считается укорененным.

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

Ответ 4

Если и родительский, и дочерний не ссылаются, но они ссылаются только друг на друга, они получают GCed.

Получите профилировщик памяти, чтобы действительно проверить ваше приложение и ответить на все ваши вопросы. Я могу порекомендовать http://memprofiler.com/

Ответ 5

Я хотел бы повторить замечание Vilx о событиях и рекомендовать шаблон дизайна, который поможет ему решить.

Скажем, что у вас есть тип, который является источником события, например:

interface IEventSource
{
    event EventHandler SomethingHappened;
}

Вот фрагмент класса, который обрабатывает события из экземпляров этого типа. Идея заключается в том, что всякий раз, когда вы назначаете новый экземпляр свойства, вы сначала отказываетесь от подписки на любое предыдущее задание, а затем подписываетесь на новый экземпляр. Нулевые проверки обеспечивают правильное поведение границ, и, более того, упрощают удаление: все, что вы делаете, - это свойство null.

Это поднимает смысл утилизации. Любой класс, который подписывается на события, должен реализовать интерфейс IDisposable, поскольку события управляются ресурсами. (N.B. Я пропустил правильную реализацию шаблона Dispose в примере для краткости, но вы получите идею.)

class MyClass : IDisposable
{
    IEventSource m_EventSource;
    public IEventSource EventSource
    {
        get { return m_EventSource; }
        set
        {
            if( null != m_EventSource )
            {
                m_EventSource -= HandleSomethingHappened;
            }
            m_EventSource = value;
            if( null != m_EventSource )
            {
                m_EventSource += HandleSomethingHappened;
            }
        }
    }

    public Dispose()
    {
        EventSource = null;
    }

    // ...
}

Ответ 6

GC может правильно справиться с круговыми ссылками, и если эти ссылки были единственными вещами, поддерживающими форму, то они будут собраны.
У меня было много проблем с .net, не возвращая память из форм. В 1.1 были некоторые ошибки, вызываемые menuitem (я думаю), что означало, что они не были утилизированы и могли утечка памяти. В этом случае добавление явного вызова для удаления и очистки переменной-члена в форме Метод Dispose сортировал проблему. Мы обнаружили, что это также помогло восстановить память для некоторых других типов управления.
Я также долгое время работал с профилировщиком CLR, рассматривая, почему формы не собирались. Насколько я мог судить, ссылки поддерживались рамками. Один на один тип формы. Поэтому, если вы создаете 100 экземпляров Form1, тогда закройте их все, только 99 будет исправлено правильно. Я не нашел способа исцелить это. С тех пор наше приложение перешло в .net 2, и это кажется намного лучше. Наша память приложений по-прежнему увеличивается, когда мы открываем первую форму и не возвращаемся вниз, когда она закрыта, но я считаю, что это связано с тем, что JIT-код и дополнительные библиотеки управления загружаются. Я также обнаружил, что, хотя GC может иметь дело с циклическими ссылками, у него, похоже, есть проблемы (иногда) с ссылками на обработчики циклических событий. IE object1 ссылается на object2 и object1 имеет метод, который обрабатывает и событие из объекта2. Я обнаружил обстоятельства, при которых это не освобождало объекты, когда я ожидал, но я никогда не мог повторить его в тестовом примере.