Сборщик мусора и финализаторы: более мелкие баллы - PullRequest
12 голосов
/ 07 марта 2011

Отвечая на другой вопрос * о SO и последующем обсуждении комментариев, я натолкнулся на стену в точке, которая мне не ясна.

Исправьте меня в любой точке, где я сбился с пути...

Когда сборщик мусора собирает объект, он вызывает финализатор этого объекта в отдельном потоке (если финализатор не был подавлен, например, с помощью метода Dispose()).Во время сбора GC приостанавливает все потоки, кроме потока, который запустил сбор (фоновый сбор в стороне).

Что не ясно:

  1. Ожидает ли сборщик мусора финализаторвыполнить на этом объекте перед его сбором?
  2. Если нет, разве он не приостанавливает потоки во время выполнения финализатора?
  3. Если он действительно ждет, что произойдет, если финализатор столкнется сблокировка удерживается одной из приостановленных нитей?Зависит ли финализатор от тупиковой ситуации?(В своем ответе я утверждаю, что это плохой дизайн, но я мог бы видеть случаи, когда это могло произойти)

* Ссылка на оригинальный вопрос: .NET GC Доступ к синхронизированному объекту из финализатора

Ответы [ 3 ]

47 голосов
/ 07 марта 2011

Ожидает ли сборщик мусора выполнения финализатора на этом объекте, прежде чем собирать его?

Ваш вопрос немного двусмысленный.

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

Итак, да,GC «ждет», пока не будет выполнен финализатор, прежде чем вернуть хранилище.Но это не ждет синхронно .Похоже, вы спрашиваете "GC синхронно вызывает финализатор прямо там?"Нет, он ставит в очередь объект, который будет завершен позже, и продолжает работу.GC хочет быстро справиться с задачей освобождения мусора и сжатия памяти, чтобы программа могла возобновить работу как можно скорее.Это не остановится, чтобы иметь дело с каким-то плаксивым объектом, который требует внимания, прежде чем он будет очищен.Он помещает этот объект в очередь и говорит: «Будьте спокойны, и поток финализатора будет иметь дело с вами позже».

Позже GC снова проверит объект и скажет: "Вы все еще мертвы? И ваш финализатор запущен?"Если ответ «да», то объект восстанавливается.(Помните, что финализатор может превратить мертвый объект обратно в живой; постарайтесь никогда этого не делать. В результате ничего приятного не происходит.)

Отключает ли он потоки, пока финализатор ещевыполняется?

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

Возможно, должны быть незамерзшие потоки, потому что финализатор может потребовать, чтобы вызов был перенаправлен в пользовательский поток в порядкеосвободить ресурс, привязанный к потоку.Конечно, некоторые из этих пользовательских потоков могут быть заблокированы или заморожены;потоки всегда могут быть заблокированы чем-то.

что произойдет, если финализатор столкнется с блокировкой, удерживаемой одним из приостановленных потоков?Зависит ли финализатор от тупиковой ситуации?

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

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

http://blogs.microsoft.co.il/blogs/sasha/archive/2010/06/30/sta-objects-and-the-finalizer-thread-tale-of-a-deadlock.aspx

Как говорится в статье: финализаторы - чрезвычайно сложный и опасный механизм очисткии вам следует избегать их, если вы можете .Невероятно легко ошибиться в финализаторе, и очень трудно понять, что правильно.

4 голосов
/ 07 марта 2011

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

GC приостанавливает все запущенные потоки. Поток финализатора, с другой стороны, будет работать в фоновом режиме, пока приложение продолжает работать. Финализатор вызывает все методы финализации для всех объектов, которые зарегистрированы для финализации. После запуска метода финализатора объекта объект будет удален из очереди, и с этой точки объект (и, возможно, все объекты, на которые он все еще ссылается) является мусором. Следующая коллекция, которая очищает объекты генерации этого объекта, (наконец) удалит этот объект. Поскольку объекты, которые живут в поколении 2, собираются примерно в 10 раз меньше, чем объекты, которые живут в поколении 1, а поколение 1 - в десять раз меньше, чем поколение 0, это может занять некоторое время, так как такой объект, наконец, собирается мусором.

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

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

Итак, чтобы ответить на ваши вопросы:

  1. Да, только после того, как объект будет удален из очереди финализатора, этот объект станет мусором, и GC соберет его.
  2. GC приостанавливает все потоки, даже очередь финализатора.
  3. Очередь финализатора может зайти в тупик. Заблокируйте как можно меньше внутри методов финализации.
3 голосов
/ 07 марта 2011

Проще всего думать о сборщике мусора как о разделении объектов на четыре группы:

  1. Те, которые недоступны ни одному корневому объекту;
  2. Те, которые доступны из списка живых финализируемых объектов, но не из любого другого корневого объекта;
  3. Те, которые находятся в списке живых финализуемых объектов, но также доступны через какой-либо корневой объект, отличный от этого списка.
  4. Те, которых нет в списке живых финализуемых объектов, но которые доступны через какой-то корневой объект, отличный от этого списка.

Когда запускается сборщик мусора, объекты типа # 1 исчезают. Объекты # 2 добавляются в список объектов, нуждающихся в скорой финализации, и удаляются из списка «живых финализируемых объектов» (таким образом, становятся объектами категории # 4). Обратите внимание, что список объектов, нуждающихся в финализации, является обычной корневой ссылкой, поэтому объекты в этом списке не могут быть собраны, пока они находятся в нем, но если к моменту завершения финализатора никакая другая корневая ссылка не будет создана, объект перейдет в категорию # 1.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...