Есть ли задержка после полной сборки мусора до того, как WeakReference.IsLive () станет false? - PullRequest
2 голосов
/ 25 ноября 2010

Я написал модульный тест, чтобы подтвердить, что «Dispose» в моем классе отключает все события и располагает таймером, который ссылается на объект.

Однако иногда WeakReference.IsLive () возвращает true, когда я ожидаю, что он вернет false?

Так есть ли задержка после полного GC перед WeakReference.IsLive () обновляется?

Если нет, можете ли вы вспомнить что-нибудь еще, что дало бы мне неповторимые результаты?

WeakReference weekJobWatchDog = new WeakReference(jobWatchDog);
jobWatchDog = null;

// not collected before Dispose called due to timer and events etc
GC.Collect(); GC.Collect();
Assert.IsTrue(weekJobWatchDog.IsAlive);

((IDisposable)weekJobWatchDog.Target).Dispose();

// is now collected as Dispose unlocked all events and dispoed the timer
GC.Collect(); GC.Collect();
Assert.IsFalse(weekJobWatchDog.IsAlive); // sometimes this fails, about 1 in 4 runs

См. также Тестирование финализаторов и IDisposable для связанного, но другого вопроса.

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

Ответы [ 3 ]

0 голосов
/ 25 ноября 2010

Эта проблема была I :

Когда вы вызываете Dispose () для System.Timers.Timer, он может вернуться до уничтожения таймера Win32. Следовательно, в неуправляемом пространстве все еще есть «корень», который поддерживает работу таймера. У времени был обработчик событий, который поддерживал мой объект.

Поскольку это очень своевременно, в большинстве случаев Таймер получал GCed, как и мой объект. Однако иногда (скажем, 1 из 10 раз) Таймер оставался в живых, как и мой объект.

Короткое выполнение Sleep () сделает мой тест успешным в 100% случаев, так что отключите событие на таймере перед его удалением, чтобы таймер не мог поддерживать мой объект в живых.

см. Также Как безопасно утилизировать System.Timers.Timer?

0 голосов
/ 16 февраля 2011

Если свойство IsLive объекта WeakReference возвращает false, это означает, что ссылка является и всегда будет kaput, и нет необходимости проверять ее значение.Если он возвращает true, это означает, что ссылка может быть живой, но никто не узнает, пока не попытается зафиксировать ее значение в сильной ссылке.Не следует полагаться на то, что WeakReference признан недействительным с какой-либо определенной степенью своевременности, и не следует принимать его значение, если он действительно не заинтересован в нем.Если кто-то делает что-то вроде очистки списка WeakReferences, удаляя те, которые умерли, свойство IsAlive позволяет идентифицировать те, которые исправны и действительно мертвы, без создания сильных ссылок на те, которые в противном случае могли бы иметь право на сборку мусора.Нет конкретной гарантии относительно того, когда любая из слабых ссылок в списке будет иметь право на очистку, но (1) такое соответствие будет более своевременным при наличии нехватки памяти;(2) в отсутствие нагрузки на память своевременность обычно не будет проблемой.

0 голосов
/ 25 ноября 2010

Поскольку метод Collect не блокирует, тогда я предполагаю - и это только предположение - что GC не собрал объект в точке, которую вы тестировали IsAlive.

(И не забывайте, что вы можете доверять IsAlive только когда он возвращает false.)

Я полагаю, что решением может быть блокирующий вызов чего-то вроде WaitForPendingFinalizers, даже если у вас нет собственных финализаторов, чтобы ждать. (Я не уверен, есть ли другие подходящие методы блокировки, которые вы могли бы использовать вместо этого.)

...