`Шаблон утилизации` в C #: зачем нам условие` if (утилизация) `? - PullRequest
1 голос
/ 09 апреля 2019

Итак, реализация по умолчанию шаблон размещения выглядит следующим образом:

class SomeClass : IDisposable
{
   // Flag: Has Dispose already been called?
   bool disposed = false;

   // Public implementation of Dispose pattern callable by consumers.
   public void Dispose()
   { 
      Dispose(true);
      GC.SuppressFinalize(this);           
   }

   // Protected implementation of Dispose pattern.
   protected virtual void Dispose(bool disposing)
   {
      if (disposed)
         return; 

      if (disposing) {
         // Free any other managed objects here.
      }

      // Free any unmanaged objects here.
      disposed = true;
   }

   ~SomeClass()
   {
      Dispose(false);
   }
}

Говорят, что:

Если вызов метода поступает из финализатора (то есть, если false), выполняется только код, освобождающий неуправляемые ресурсы. Так как порядок, в котором сборщик мусора уничтожает управляемые объекты во время финализации не определено, вызывая эту Dispose перегрузку с значение false не позволяет финализатору пытаться выпустить управляемый ресурсы, которые, возможно, уже были восстановлены.

Вопрос: почему предполагается, что объекты, на которые ссылается объект SomeClass, возможно, уже были освобождены, и мы не должны пытаться утилизировать их, когда метод вызывается из финализатор? Если на эти объекты все еще ссылается наш SomeClass объект, они не могут быть освобождены, не правда ли? Говорят, что:

Те, кто с ожидающими (не запущенными) финализаторами остаются в живых (пока) и поставить на особую очередь. [...] Перед финализатором каждого объекта работает, это все еще очень живо - эта очередь действует как корень объект .

Итак, опять же, на наш SomeClass объект ссылается эта очередь (что совпадает с тем, на который ссылается корень). И другие объекты, на которые ссылается объект SomeClass, также должны быть живы (так как они внедрены через объект SomeClass). Тогда почему и как они могли быть освобождены к моменту вызова финализатора SomeClass?

Ответы [ 2 ]

5 голосов
/ 10 апреля 2019

Конрад Кокоса имеет внушительное объяснение своей книги Pro .NET Memory Management .(выделение добавлено)

Во время GC, в конце фазы Mark, GC проверяет очередь финализации, чтобы увидеть, не умер ли какой-либо из финализируемых объектов.Если они некоторые, их еще нельзя удалить, потому что их финализаторы нужно будет выполнить .Следовательно, такой объект перемещен в еще одну очередь с именем fReachable queue .Его название происходит от того факта, что оно представляет объекты, достижимые для финализации - те, которые теперь доступны только благодаря финализации .Если такие объекты найдены, GC указывает выделенному потоку финализатора, который нужно сделать.

Поток финализации - это еще один поток, созданный средой выполнения .NET. Он удаляет объекты из очереди fReachable один за другим и вызывает их финализаторы .Это происходит после того, как GC возобновляет управляемые потоки, потому что коду финализатора может потребоваться выделить объекты. Поскольку единственный корень этого объекта удаляется из очереди fReachable, следующий GC, осуждающий поколение, в котором находится этот объект, обнаружит, что он недоступен, и восстановит его.

Кроме того, fReachable очередь рассматривается как корень, рассматриваемый на этапе Mark, поскольку поток финализатора может быть недостаточно быстрым для обработки всех объектов из него между GC .Это подвергает финализируемые объекты в большей степени кризису среднего возраста - они могут оставаться в очереди fReachable какое-то время, потребляя поколение 2 только из-за незавершенной финализации.

Я думаю, что ключ здесь:

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

0 голосов
/ 09 апреля 2019

Объекты в .NET существуют, пока существуют ссылки на них. Они перестают существовать, как только последняя ссылка делает это. Хранилище, используемое объектом, никогда не будет восстановлено, пока объект существует, но GC делает несколько вещей, прежде чем он освобождает хранилище:

  1. Существует специальный список, называемый «очередь финализатора», который содержит ссылки на все объекты, которые имеют зарегистрированные финализаторы. После определения любой другой ссылки, которая существует где-либо в юниверсе, GC проверит все объекты в очереди финализатора, чтобы увидеть, были ли найдены какие-либо ссылки на них. Если этот процесс заставляет его найти объект, который не был обнаружен ранее, он копирует ссылку на другой список, называемый «свободная очередь». Каждый раз, когда свободная очередь не пуста и не выполняется финализатор, система извлекает ссылку из этой очереди и вызывает для нее вызов finalize.

  2. ГК также проверяет цели всех слабых ссылок и делает недействительными любые слабые ссылки, цель которых не была идентифицирована ни одной живой сильной ссылкой.

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

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

...