.NET: способ определить, есть ли у объекта какие-либо ссылки на него?

Вопрос. Есть ли способ узнать, есть ли у объекта "сильные ссылки" на него?


Рэймонд Чен намекнул, что решение возможно :

Вы хотите знать, равен ли счетчик ссылок нулю или отличному от нуля. Для этого используйте WeakReference.

Примечания

  • у меня есть "слабая ссылка" на объект (с использованием WeakReference). Если бы у меня была сильная ссылка, ответ был бы немедленно: "Да. У вас есть сильная ссылка на объект".
  • сборщик мусора не предоставляет ответов
  • свойство IsAlive может сообщить вам, только если объект были собраны или нет. Нет, если есть сильные ссылки на это, или нет. (Объект без ссылок можно разобрать - GC просто еще не дошел до него)
  • объекты в .NET не учитываются при подсчете ссылок
  • не все объекты должны реализовывать интерфейс IDisposable.
  • не все объекты мои

Пример кода

Этот пример кода демонстрирует проблемы, связанные с принудительной сборкой мусора и свойством WeakReference IsAlive, чтобы определить, есть ли у объекта какие-либо необработанные ссылки на него.

WeakReference m_wr = null;

...

for (int i = 0; i < 1000000; i++)
{
   Pig p = new Pig();
   m_wr = new WeakReference(p);
}

...

GC.Collect();
if (m_wr.IsAlive)
   Environment.FailFast("All objects should have been collected by now");

person Ian Boyd    schedule 12.08.2010    source источник
comment
зачем вам это знать?   -  person Tim Robinson    schedule 12.08.2010
comment
Для этого используйте WeakReference — Рэймонд намекает на WeakReference.IsAlive   -  person Tim Robinson    schedule 12.08.2010
comment
@Tim Robinson Если IsAlive возвращает true, все, что я могу сделать, это то, что объект не был собран. Если IsAlive возвращает false, все, что я могу сделать, это то, что объект был собран. я хочу знать, есть ли у объекта сильные ссылки на него. Объект с сильными ссылками не будет собран, но не все несобранные объекты имеют на них сильные ссылки.   -  person Ian Boyd    schedule 12.08.2010
comment
Зачем мне это знать? я мог бы придумать несколько причин 1. Для улучшения всего человечества, чтобы расширить горизонт человеческих знаний 2. я выполняю диссертацию о физических эффектах деградации объектов с сильными ссылками в оперативной памяти DDR в системах с пониженной тактовой частотой. 3. я хочу знать, безопасно ли вызывать Dispose при подключении к базе данных. 4. я хочу поймать ошибку, когда кто-то ссылается на объект, но я уверен, что никто не должен этого делать. 5. Потому что кто-то еще спрашивал, но люди отказывались отвечать на вопрос.   -  person Ian Boyd    schedule 12.08.2010
comment
Совет @Mark позвонить GC.Collect; WR.IsAlive хорош. Единственный верный способ ответить, где все сильные ссылки? заключается в выполнении сборки мусора. Альтернативой может быть приостановка программы, отслеживание корней сборщика мусора и обход ссылок на объекты самостоятельно, но сборщик мусора все равно делает именно это.   -  person Tim Robinson    schedule 12.08.2010


Ответы (3)


Нет, не без использования API отладчика.

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

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

person Jon Skeet    schedule 12.08.2010
comment
Нет, был ответ, который я предположил. Посмотрим, не согласится ли кто. Но с твоим 205 тысячами репутации, я думаю, ты станешь авторитетом в этом вопросе. - person Ian Boyd; 12.08.2010
comment
Вы можете попробовать добавить неявное преобразование к объекту, так как weakreference принимает его как параметр и добавляет счетчик в этот метод неявного преобразования, но все остальные вещи, которые получают его как объект, также увеличивают его. Также неявное преобразование в объект может бесконечно повторяться. Золотые медали за очко тоже важны. - person huseyin tugrul buyukisik; 26.03.2017
comment
@huseyintugrulbuyukisik: Вы не можете добавить неявное преобразование к object, и, честно говоря, я не понимаю, как это поможет. И я не понимаю последнюю часть вашего комментария... - person Jon Skeet; 26.03.2017
comment
@JonSkeet это из-за сборки мусора? Последняя часть, я имею в виду более частые золотые медали, выглядит круто, и вы выдержали частоту для 530 медалей. - person huseyin tugrul buyukisik; 26.03.2017
comment
@huseyintugrulbuyukisik: Что из-за сборки мусора? Запрет неявных преобразований в объект? Нет, совсем нет. - person Jon Skeet; 26.03.2017

Сначала вызовите GC.Collect() и затем проверьте WeakReference.IsAlive. Если это true (т. е. он не был собран после вызова GC.Collect), то где-то есть сильная ссылка.

person Mark Cidade    schedule 12.08.2010
comment
+1 за правильность. Однако, если вы вызовете GC.Collect для проверки счетчиков ссылок на объекты, вы переместите объекты в более поздние поколения, что продлит их жизнь, если вы когда-нибудь перестанете вручную вызывать GC. Таким образом, это плохая практика. - person Amy B; 12.08.2010
comment
Не технически правильно. То, что форсирование сбора нулевого поколения не гарантирует, что рассматриваемый объект будет собран: он мог быть переведен в более высокое поколение. - person Ian Boyd; 12.08.2010
comment
Это будет повышено до более высокого поколения только в том случае, если все еще есть невыполненные ссылки. Если сборщик мусора определит, что ссылок нет, он будет оставлен в покое или собран. - person Tim Robinson; 12.08.2010
comment
@ Тим Робинсон Это не совсем так. Существует эвристика, связанная с продвижением объектов к более высоким поколениям. Это не то, на что вы можете рассчитывать сейчас. И уж точно не задокументировано, что завтра не изменится. Кроме того, объекты, никогда не собираемые, являются допустимым сценарием (т. е. сборщик мусора null является допустимым сборщиком мусора). Ничто не говорит о том, что сборщик мусора должен что-то делать, когда вы вызываете Collect — это внутренняя деталь реализации. - person Ian Boyd; 12.08.2010
comment
@Ian: MSDN говорит, что GC.Collect вызывает немедленную сборку мусора всех поколений. msdn.microsoft.com/en-us/library/xe0c2357.aspx - person Mark Cidade; 12.08.2010
comment
@Mark Cidade GC не может ничего делать. Кроме того, поскольку он имеет резьбу, это состояние гонки. - person Ian Boyd; 12.08.2010
comment
Где сказано, что .NET GC может ничего не делать при вызове GC.Collect()? Аргумент Чена о нулевом GC носит чисто теоретический характер. И я не понимаю, как это состояние гонки, поскольку GC.Collect() не возвращается, пока не завершит сбор. - person Mark Cidade; 12.08.2010
comment
@Mark Cidade Не сказано, что GC может ничего не делать при вызове GC.Collect(); и не говорит, что будет делать GC. В нем говорится, что принудительно выполняется немедленная сборка мусора всех поколений.). Оптимизация, которой разрешено существовать там, где она ничего не делает. Хорошо, я выполнил операции сбора — и ничего не собрал, так как нет нехватки памяти. - person Ian Boyd; 13.08.2010
comment
Обратите внимание, что проверка IsAlive на true является состоянием гонки. См. примечания в документации: поскольку объект потенциально может быть утилизирован для сборки мусора сразу после того, как свойство IsAlive возвращает значение true, использование этого свойства не рекомендуется, если только вы не тестируете только возвращаемое значение false. - person Jack Ukleja; 21.04.2017
comment
Почти 7 лет назад мы установили, что это ненадежный метод. - person Mark Cidade; 21.04.2017

Вы должны прочитать публикацию Раймонда Чена о ссылке считает со вчерашнего дня. После этого вы должны решить, действительно ли вам нужно это сделать и почему. Тогда вернитесь и расскажите нам, почему.

Хм, кажется, вы прочитали пост - впитали в себя мелкие детали и упустили суть.


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

Читайте документы. Dispose вызывает Close. Закрыть безопасно вызывать сколько угодно.

Приложение может вызывать Close несколько раз. Исключение не генерируется.

person Amy B    schedule 12.08.2010
comment
Все реализации IDisposable.Dispose должны вести себя хорошо, если вызываются более одного раза. Кроме того, финализаторы также должны вести себя так же, потому что уже финализированные объекты могут быть воскрешены и повторно финализированы. - person Tim Robinson; 12.08.2010
comment
я добавил комментарий к исходному вопросу с указанием, почему. Вы найдете мое имя в комментариях к этому сообщению в блоге, где я задаю тот же вопрос — вот тогда я и решил задать его здесь. Кроме того, двойной вызов Dispose или Close при подключении к базе данных — не самое худшее, что может случиться. - person Ian Boyd; 12.08.2010
comment
Кроме того, важный момент, я не спрашивал о количестве ссылок. - person Ian Boyd; 12.08.2010