Почему GC помещает объекты в очередь финализации?

Как я понимаю, сборщик мусора в С# поместит все объекты класса в очередь финализации, как только я реализую деструктор класса. Когда я читал документацию для GC.Suppresfinalize, он упоминает, что заголовок объекта уже имеет бит для завершения завершения вызова.

Мне интересно, почему разработчикам GC пришлось поставить все объекты в очередь и задержать освобождение памяти на 1-2 цикла. Не могли ли они просто взглянуть на флаг бита при освобождении памяти, затем вызвать финализацию объекта и затем освободить память?

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

EDIT: если флаг бит для suppressfinalize, разработчики GC могли бы добавить в этот заголовок объект еще один флаг, no?

Ответ 1

Таким образом, он может работать в другом потоке и, таким образом, не блокировать основной поток GC.

Вы можете много узнать о GC из этой статьи MSDN.

Ответ 2

Здесь есть большое объяснение.

Что такое конечная точка Finalizer и Control + ThreadMethodEntry?

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

Ответ 3

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

Ответ 4

Сборщик мусора не идентифицирует и не проверяет мусор, кроме, возможно, при обработке кучи больших объектов. Вместо этого его поведение похоже на поведение пинсеттера для боулинга, снимающего мертвое поле между бросками: пинсеттер захватывает все штифты, которые все еще стоят, поднимает их с поверхности полосы и затем беспроблемно пробегает полосу подметания через переулок за сколько контактов на этой поверхности. Сметание оптовой памяти намного быстрее, чем определение отдельных объектов, которые нужно удалить. Если 1% объектов имеют финализаторы (реальное число, вероятно, даже меньше), тогда необходимо будет изучить 100 заголовков объектов, чтобы найти каждый объект, подлежащий окончательному окончанию. Наличие отдельного списка объектов, которые имеют финализаторы, делает ненужным для GC даже смотреть на любые объекты мусора, которые этого не делают.

Ответ 5

@Jason: это верно для f-достижимой очереди. Но IMHO не объясняет, почему существует окончательная версия.

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

Флаг завершения в заголовке объекта говорит: "объект должен быть финализирован" или "объект не должен быть финализирован", но он не говорит, что финализация уже произошла.

Но, честно говоря, я не понимаю, зачем это нужно в текущей реализации процесса завершения.

В самом деле, вот наивный рабочий процесс, который я себе представляю без очереди финализации:

  • при создании объекта, если он имеет финализатор, GC устанавливает флаг финализации;
  • если вызывается SupressFinalize, тогда флаг обнуляется;
  • теперь можно перейти к тому, когда GC собирает объект, на который не ссылаются нигде: если установлен флаг завершения, GC помещает ссылку на объект в f-достижимую очередь и разрешает поток финализации;
  • позже поток финализации отменяет ссылку, сбрасывает флаг завершения и запускает финализатор;
  • Если объект хочет быть refinalized позже, он может ReRegisterForFinalize снова установить флаг завершения;
  • позже GC снова собирает объект: если флаг завершения не задан, он знает, что делать нечего, а затем освобождает память объекта;
  • Если флаг финализации установлен, GC снова помещает ссылку на объект в f-достижимую очередь, и мы снова переходим к другому раунду;
  • в какой-то момент времени объект счастлив, завершает финализацию и собирается; или домен приложения или процесс завершаются, и память все равно освобождается.

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

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

Но на самом деле я не думаю, что реализация GC основана на догматическом применении таких теоретических правил, но только на прагматическом выборе; поэтому очевидно, что мне не хватает некоторых ключевых сценариев, где GC нуждается в очереди финализации, чтобы знать, что делать при сборе объекта, но какие?