Использование воскрешения объекта

У меня проблема с утечками памяти в моем приложении для Windows.NET. Поэтому я начал читать статьи об управлении памятью в .NET. И я нашел интересную практику в одной из статей Джеффри Рихтера. Это название практики - "воскрешение объекта". Похоже, что локальный код инициализирует глобальную или статическую переменную "this":

protected override void Finalize() {
     Application.ObjHolder = this;
     GC.ReRegisterForFinalize(this);
}

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

Ответы

Ответ 1

Спекулятивный: в ситуации с пулом, например ConnectionPool.

Вы можете использовать его для восстановления объектов, которые были неправильно размещены, но к которым код приложения больше не содержит ссылку. Вы не можете хранить их в списке в пуле, потому что это блокирует сбор GC.

Ответ 2

Из той же статьи: "Очень мало пользы от воскрешения, и вы действительно должны избегать его, если это возможно".

Лучшее использование, о котором я могу думать, - это шаблон "рециркуляции". Рассмотрим Factory, который создает дорогие, практически неизменные объекты; например, объекты, созданные путем анализа файла данных или путем отражения сборки или глубокого копирования графа объектов "хозяин". Результаты вряд ли будут меняться каждый раз, когда вы будете выполнять этот дорогостоящий процесс. В ваших интересах избегать создания экземпляра с нуля; однако по некоторым причинам проектирования система должна иметь возможность создавать множество экземпляров (без синглотонов), и ваши потребители не могут знать о Factory, чтобы они могли "возвращать" сам объект; они могут иметь объект, введенный, или получить делегат метода Factory, из которого они получают ссылку. Когда зависимый класс выходит за пределы области видимости, обычно экземпляр также будет выглядеть.

Возможным ответом является переопределить Finalize(), очистить любую часть изменяемого состояния экземпляра, а затем, пока объект Factory находится в области видимости, повторно присоедините экземпляр к некоторому члену Factory. Это позволяет процессу сбора мусора, по сути, "перерабатывать" ценную часть этих объектов, когда они в противном случае выходят из сферы действия и полностью уничтожаются. Factory может посмотреть и посмотреть, есть ли у него какие-либо переработанные объекты, доступные в нем "bin", и если да, то он может отполировать его и передать его. Factory должен был бы только создать экземпляр новой копии объекта, если число общих объектов, используемых процессом, увеличилось.

Другие возможные варианты использования могут включать в себя некоторые узкоспециализированные логгеровские или аудиторские реализации, где объекты, которые вы хотите обработать после их смерти, присоединятся к рабочей очереди, управляемой этим процессом. После того, как процесс обрабатывает их, они могут быть полностью уничтожены.

В общем, если вы хотите, чтобы иждивенцы ДУМАЛИ, они избавляются от объекта или не должны беспокоиться, но вы хотите сохранить экземпляр, воскрешение может быть хорошим инструментом, но вам придется смотреть ОЧЕНЬ тщательно, чтобы избежать ситуаций, когда объекты, получающие воскрешенные ссылки, становятся "паковыми крысами" и сохраняют каждый экземпляр, который когда-либо был создан в памяти на протяжении всего процесса.

Ответ 3

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

Говоря об этом, я вообще избегаю этого на практике.

Ответ 4

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

Они реализовали репозиторий объектов, содержащий "удаленные" экземпляры объектов. Перед созданием нового объекта они сначала проверили бы, существует ли в репозитории уже существовавший.

Компромиссным было увеличение потребления памяти (так как одновременно могло существовать много неиспользуемых объектов) для повышения производительности (поскольку общее количество объектов было уменьшено).

Обратите внимание, что решение о внедрении этого шаблона было основано на узких местах, которые они наблюдали посредством профилирования в своем конкретном сценарии. Я ожидаю, что это будет исключительным обстоятельством.

Ответ 5

Для того, что я знаю .net вызывает финализаторы в определенном порядке. Если ваш класс содержит ссылки на другие объекты, они могут быть завершены (и, следовательно, выбраны), когда вызывается ваш финализатор. Если вы затем решите воскресить свой объект, у вас будут ссылки на финализированные/удаленные объекты.

class A {
  static Set<A> resurectedA = new Set<A>();
  B b = new B();
  ~A() {
    //will not die. keep a reference in resurectedA.
    resurectedA.Add(this);
    GC.ReRegisterForFinalize(this); 

    //at this point you may have a problem. By resurrecting this you are resurrecting b and b Finalize may have already been called.
  } 
}
class B : IDisposable {
  //regular IDisposable/Destructor pattern http://msdn.microsoft.com/en-us/library/b1yfkh5e(v=vs.110).aspx
}