Почему сборщик мусора не может понять, когда объекты, ссылающиеся друг на друга, действительно осиротели? - PullRequest
6 голосов
/ 03 августа 2009

Я понимаю, что в управляемом языке, таком как Java или C #, существует такая вещь, называемая сборщиком мусора, которая время от времени проверяет, есть ли экземпляры объектов, на которые больше нет ссылок, и, таким образом, они полностью осиротели, и очищает затем из памяти. Но если на два объекта не ссылаются никакие переменные в программе, а ссылаются друг на друга (например, на подписку на событие), сборщик мусора увидит эту ссылку и не очистит объекты из памяти.

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

Ответы [ 5 ]

24 голосов
/ 03 августа 2009

Ваше предположение неверно. Если ГХ «видит» (см. Ниже) циклическую ссылку на 2 или более объектов (на этапе «Пометить»), но на них не ссылаются никакие другие объекты или постоянные дескрипторы ГХ (сильные ссылки), эти объекты будут собираться (во время фаза «развертки»).

Подробное описание сборщика мусора CLR можно найти в этой статье MSDN и в этом блоге .

Примечание. Фактически, GC даже не "видит" эти типы объектов во время фазы mark , поскольку они недоступны и, следовательно, собираются во время развертки .

7 голосов
/ 03 августа 2009

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

2 голосов
/ 03 августа 2009

Это главный недостаток традиционного подсчета ссылок на сборку мусора . Свойство сборщика мусора, описывающее это поведение, является неполным сборщиком. Другие сборщики в значительной степени попадают в категорию под названием трассировка сборщиков мусора , которая включает в себя традиционную разметку, полупространство / сжатие и гибриды поколений, и не страдает от этих недостатков (но сталкивается с несколькими другими).

Все реализации JVM и CLI, о которых я знаю, используют полные сборщики , что означает, что они не страдают от конкретной проблемы, о которой вы здесь спрашиваете. Насколько мне известно, из этих Jikes RVM является единственным, снабжающим коллектор подсчета ссылок (один из его многочисленных).

Еще одна интересная вещь, на которую следует обратить внимание, - это решение проблемы полноты при сборке мусора при подсчете ссылок, и получившиеся в результате сборщики демонстрируют некоторые интересные характеристики производительности, которые трудно получить при трассировке сборщиков. К сожалению, самые эффективные алгоритмы сбора мусора с подсчетом ссылок и большинство модификаций полноты полагаются на помощь компилятора, поэтому перенести их в C ++ shared_ptr<T> сложно / не происходит. Вместо этого у нас есть weak_ptr<T> и документированные правила (извините за неоптимальную ссылку - очевидно, документация ускользает от меня) о простом избежании проблем. Это не первый раз (еще одна посредственная ссылка), мы видели этот подход, и надежда на то, что дополнительная работа по предотвращению проблем с памятью меньше, чем объем работы, необходимый для поддержки кода, который не ' т shared_ptr<T> и т. д.

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

2 голосов
/ 03 августа 2009

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

Присоединяйтесь, например. на событие App.Idle в Windows Forms, и ваш объект будет поддерживаться в течение оставшегося времени жизни приложения. Зачем? Это статическое приложение будет содержать ссылку (хотя и косвенно, через делегата) на вашего зарегистрированного наблюдателя. Даже если вы избавились от своего наблюдателя, он все еще привязан к App.Idle. Вы можете построить множество таких примеров.

1 голос
/ 03 августа 2009

Другие ответы здесь, безусловно, правильны; .NET выполняет сборку мусора на основе достижимости объекта.

То, что я хотел добавить: я могу порекомендовать прочитать Общие сведения о сборке мусора в .NET (простая статья Эндрю Хантера), если вам нужна более подробная информация.

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