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

GC.Collect() и Finalize

Хорошо, он знал, что GC неявно вызывает методы Finalize для объектов, когда он идентифицирует этот объект как мусор. Но что произойдет, если я сделаю GC.Collect()? Готовы ли финализаторы? Возможно, был глупый вопрос, но кто-то спросил меня об этом, и я ответил "Да", а затем подумал: "Это было полностью правильно?"

4b9b3361

Ответ 1

Хорошо, он знал, что GC неявно вызывает методы Finalize для объектов, когда он идентифицирует этот объект как мусор.

Нет, нет. Это неизвестно, потому что для того, чтобы быть знанием, утверждение должно быть правдой. Это утверждение неверно. Сборщик мусора не запускает финализаторы по мере того, как он трассирует, независимо от того, запускается он сам или вы вызываете Collect. В финализаторе поток запускает финализаторы после того, как сборщик трассировки нашел мусор, и это происходит асинхронно по отношению к вызову Collect. (Если это вообще происходит, а это не так, как указывает другой ответ). То есть вы не можете полагаться на выполнение потока финализатора, прежде чем управление вернется с Collect.

Здесь приведен упрощенный пример того, как он работает:

  • Когда происходит сбор, поток трассировки сборщика мусора отслеживает корни - объекты, которые, как известно, живы, и каждый объект, к которому они относятся, и т.д. - для определения мертвых объектов.
  • Объекты "Мертвые", которые ждут финализаторы, переносятся в очередь финализатора. Очередь финализатора - это корень. Поэтому эти "мертвые" объекты на самом деле все еще живы.
  • Нить финализатора, которая, как правило, отличается от потока трассировки GC, в конечном итоге запускает и освобождает очередь финализатора. Затем эти объекты становятся действительно мертвыми и собираются в следующей коллекции на трассировочном потоке. (Конечно, поскольку они только что пережили первую коллекцию, они могут быть в более высоком поколении.)

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

Вызовите aptly named WaitForPendingFinalizers после вызова Collect, если вы хотите гарантировать выполнение всех финализаторов. Это приостановит текущий поток до тех пор, пока поток финализатора не начнет опустошать очередь. И если вы хотите, чтобы эти завершенные объекты восстановили свою память, вам придется вызывать Collect второй раз.

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

Ответ 2

Собственно ответ "Это зависит". На самом деле есть выделенный поток, который выполняет все финализаторы. Это означает, что вызов GC.Collect вызвал только этот процесс, и выполнение всех финализаторов будет вызываться асинхронно.

Если вы хотите подождать, пока все финализаторы будут вызваны, вы можете использовать следующий трюк:

GC.Collect();
// Waiting till finilizer thread will call all finalizers
GC.WaitForPendingFinalizers();

Ответ 3

Да, но не сразу. Этот отрывок из Сбор мусора: автоматическое управление памятью в Microsoft.NET Framework (журнал MSDN) (*)

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

Когда происходит GC... сборщик мусора сканирует завершение очередь ищет указатели на эти объекты. Когда указатель найден, указатель удаляется из очереди финализации и добавляется к freachable queue (произносится как F-достижимая). Свободная очередь другая внутренняя структура данных, управляемая сборщиком мусора. Каждый указатель в freachable queue идентифицирует объект, который является готов к вызову метода Finalize.

Существует специальный поток времени выполнения, посвященный вызову Finalize методы. Когда свободная очередь пуста (обычно это случай), эта нить спит. Но когда появляются записи, эта нить просыпается, удаляет каждую запись из очереди и вызывает каждый объект Finalize метод. Из-за этого вы не должны выполнять какой-либо код в Finalize метод, который делает любое предположение о потоке, выполняющем код. Например, избегайте доступа к локальному хранилищу потоков в Метод завершения."

(*) С ноября 2000 года, возможно, с тех пор все изменилось.

Ответ 4

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

Если вы не назовете GC.WaitForPendingFinalizers(), финализаторы могут продолжить выполнение в фоновом режиме задолго после завершения сборки мусора.


Кстати, нет никакой гарантии, что финализаторы будут называться вообще. Из MSDN...

Метод Finalize может не запускаться или не запускаться при все в следующих исключительных обстоятельствах:

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

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

Ответ 5

Здесь можно указать пару пунктов.

Finalizer - это последняя точка, где объекты .net могут освобождать неуправляемые ресурсы. Финализаторы должны выполняться только в том случае, если вы неправильно распоряжаетесь своими экземплярами. В идеале финализаторы никогда не должны выполняться во многих случаях.. Поскольку правильная реализация реализации должна подавить финализацию.

Вот пример правильной реализации IDispoable.

Если вы вызываете метод Dispose для любых одноразовых объектов, он должен очищать все ссылки и подавлять завершение. Если есть не очень хороший разработчик, который забыл вызвать метод Dispose, Finalizer - это спасатель жизни.