В своей книге Троелсен пишет: ...детали, касающиеся сборки мусора (вроде слабых ссылок и восстановления объектов), остались не рассмотренными... Где бы можно в доступно виде прочитать про это? Гуглил "Object resurrection", но ничего хорошего на русском вообще не нашел.
Ответ Ну, раз не нашли, давайте я расскажу. Объекты делятся на те, которым нужна финализация (то есть, те, у которых есть деструктор-финализатор), и те, которые обходятся без неё. Вторых объектов гораздо больше: обычно вам не нужен деструктор в объекте. Такие объекты нам не интересны, пропускаем их. Итак, что происходит во время пробега сборщика мусора? Он находит все объекты, на которые нету корневых ссылок (возможно, косвенных) и ссылок из «живых» локальных переменных. Эти объекты помещаются в список на уничтожение. Теперь, те из них, которым нужна финализация¹, перемещаются в отдельную очередь для финализации. Остальные объекты просто уничтожаются². Заметьте, что очередь финализации является корневым объектом, поэтому элементы, которые уже там лежат, не являются кандидатами на удаление. Отдельный поток³ следит за очередью финализации, и понемногу финализирует объекты оттуда. Это значит, что у этих объектов вызывается финализатор. После этого объект удаляется из очереди, и таким образом подлежит повторной сборке мусора. Больше очередь финализации не ссылается на объект, и он в принципе должен быть собран сборщиком мусора при следующем пробеге. Однако, финализатор — это такая же функция⁴. И эта функция может сделать что-то, что сделает объект недоступным для сборщика мусора! Например, поместит ссылку на этот объект в глобальную переменную. Таким образом, у нас получается воскресший объект, который к тому же уже профинализирован! CLR запоминает, что объект уже был финализирован, и больше не будет пытаться его финализировать снова. (Вы можете сбросить этот флаг при помощи GC.ReRegisterForFinalize, устроив потенциально бесконечный цикл.) Заметьте, что даже если вы полностью контролируете код своего объекта, вы не можете предотвратить «оживление» вашего объекта другим объектом! Смотрите: если другой финализируемый объект содержит ссылку на ваш, и они оба попадают в очередь финализации, и этот самый другой объект в результате финализации оживает, то он «тянет за собой» с того света назад на этот все объекты, доступные по его ссылкам! В частности, и ваш объект. Несмотря на то, что восстановление объектов, пойманных сборщиком мусора, возможно, эта техника очень сложная, опасная и практически на грани фола. Поэтому я бы предостерёг от её использования для целей, отличных от простого развлечения. Если вам захотелось оживить объект, возможно, вам просто нужен явно управляемый пул объектов? PS: Учитывая сложности корректной финализации, стоит просто избегать её по возможности. Если вы используете финализатор для работы с неуправляемыми ресурсами, то достаточно вместо IntPtr использовать SafeHandle, и необходимость в финализации отпадёт. Обратите внимание, что не стоит доверять финализатору ещё и по такой причине: функции GC.SuppressFinalize() и GC.ReRegisterForFinalize() для вашего объекта может вызвать любой код!
¹ Вы можете, однако, запретить финализацию даже для объекта с финализатором при помощи метода GC.SuppressFinalize. Это полезно, например, если вы имплементируете IDisposable, и в методе Dispose подчищаете все ресурсы. ² Если только на них нету ссылок из объектов, помещаемых в очередь для финализации, т. к. эти объекты после помещения в очередь снова имеют корневую ссылку! ³ В текущей реализации подсистемы сборки мусора, разумеется. ⁴ С некоторыми предосторожностями: в финализаторе нужно с осторожностью работать, например, с управляемыми полями, потому что их содержимое может быть уже финализировано!