Как я могу написать единичный тест, чтобы определить, можно ли собирать мусор?

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

Можно ли это сделать надежным способом?

РЕДАКТИРОВАТЬ

В настоящее время у меня есть следующий тест, основанный на ответе Пола Стовелла, который преуспевает:

[TestMethod] public void ReleaseTest() { WindsorContainer container = new WindsorContainer(); container.Kernel.ReleasePolicy = new NoTrackingReleasePolicy(); container.AddComponentWithLifestyle(LifestyleType.Transient); Assert.AreEqual(0, ReleaseTester.refCount); var weakRef = new WeakReference(container.Resolve()); Assert.AreEqual(1, ReleaseTester.refCount); GC.Collect(); GC.WaitForPendingFinalizers(); Assert.AreEqual(0, ReleaseTester.refCount, "Component not released"); } private class ReleaseTester { public static int refCount = 0; public ReleaseTester() { refCount++; } ~ReleaseTester() { refCount--; } } 

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

Это то, что я обычно делаю:

 [Test] public void MyTest() { WeakReference reference; new Action(() => { var service = new Service(); // Do things with service that might cause a memory leak... reference = new WeakReference(service, true); })(); // Service should have gone out of scope about now, // so the garbage collector can clean it up GC.Collect(); GC.WaitForPendingFinalizers(); Assert.IsNull(reference.Target); } 

NB: Очень много раз, когда вы должны вызвать GC.Collect () в производственном приложении. Но тестирование на утечки – один из примеров того, где это уместно.

Возможно, вы могли бы провести с ним WeakReference, а затем проверить, чтобы после завершения тестов он не был жив (т.е. IsAlive).

Основываясь на ответе Павла , я создал более многоразовый метод Assert. Поскольку string ‘s копируются по значению, я добавил для них явную проверку. Они могут быть собраны сборщиком мусора.

 public static void IsGarbageCollected( ref TObject @object ) where TObject : class { Action emptyAction = o => { }; IsGarbageCollected( ref @object, emptyAction ); } public static void IsGarbageCollected( ref TObject @object, Action useObject ) where TObject : class { if ( typeof( TObject ) == typeof( string ) ) { // Strings are copied by value, and don't leak anyhow. return; } int generation = GC.GetGeneration( @object ); useObject( @object ); WeakReference reference = new WeakReference( @object, true ); @object = null; // The object should have gone out of scope about now, // so the garbage collector can clean it up. GC.Collect( generation, GCCollectionMode.Forced ); GC.WaitForPendingFinalizers(); Assert.IsNull( reference.Target ); } 

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

 [TestMethod] public void IsGarbageCollectedTest() { // Empty object without any references which are held. object empty = new object(); AssertHelper.IsGarbageCollected( ref empty ); // Strings are copied by value, but are collectable! string @string = ""; AssertHelper.IsGarbageCollected( ref @string ); // Keep reference around. object hookedEvent = new object(); #pragma warning disable 168 object referenceCopy = hookedEvent; #pragma warning restore 168 AssertHelper.ThrowsException( () => AssertHelper.IsGarbageCollected( ref hookedEvent ) ); GC.KeepAlive( referenceCopy ); // Still attached as event. Publisher publisher = new Publisher(); Subscriber subscriber = new Subscriber( publisher ); AssertHelper.ThrowsException( () => AssertHelper.IsGarbageCollected( ref subscriber ) ); GC.KeepAlive( publisher ); } 

Из-за различий при использовании конфигурации Release (я предполагаю, что оптимизация компилятора) некоторые из этих модульных тестов терпят неудачу, если GC.KeepAlive() не следует вызывать.

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

Используйте платформу dotMemory Unit (бесплатно)

 [TestMethod] public void ReleaseTest() { // arrange WindsorContainer container = new WindsorContainer(); container.Kernel.ReleasePolicy = new NoTrackingReleasePolicy(); container.AddComponentWithLifestyle(LifestyleType.Transient); var target = container.Resolve() // act target = null; // assert dotMemory.Check(memory => Assert.AreEqual( 0, memory.GetObjects(where => where.Type.Is().ObjectsCount, "Component not released"); } 

Это не ответ, однако вы можете попробовать запустить свой код в режимах Debug и Release (для сравнения).

По моему опыту, отладочная версия JIT-кода упрощена для отладки и, следовательно, может видеть, что ссылки остаются живыми дольше (область видимости функции). Однако код JITed в режиме Release может быстро собирать объекты для сбора, и если происходит сбор.

Также не отвечая на ваш вопрос: 🙂
Мне было бы интересно увидеть, как вы отлаживаете этот код с помощью Visual Studio в режиме Interop (Managed and Native), а затем разбиваетесь после отображения windows сообщения или чего-то еще. Затем вы можете открыть Debug-> Windows-Immediate, а затем ввести

 load sos (Change to thread 0) !dso !do  !gcroot  (and look for any roots) 

(или вы можете использовать Windbg как другие, опубликованные в предыдущих сообщениях)

Спасибо, Аарон

  • Проекты NUnit vs Visual Studio 2008 для тестирования модhive?
  • Чтобы проверить настраиваемую директиву angularjs validation
  • Как написать модульный тест?
  • Запустите один тестовый метод с maven
  • Что делает хороший модульный тест?
  • Единичное тестирование DbContext
  • Моческое статическое свойство с moq
  • Можно ли запустить экземпляр сервера zookeeper в процессе, скажем, для модульных тестов?
  • Selenium many Logs (Как удалить)
  • В чем разница между фальсификацией, насмешкой и ступлением?
  • Как начать работу с GTest и CMake
  • Interesting Posts

    Android Geofence в конечном итоге прекратит получать переход

    Как вы центрируете свое главное окно в WPF?

    Объявление переменных внутри оператора switch

    Корзина на пересаженном диске

    C обратных битов в целые числа без знака

    Как изменить цвет шрифта для отключенного ввода?

    Переменные стека против переменных кучи

    Объединение по диапазону в R – Применение циклов

    USB 3.0 флэш-накопитель не будет работать в режиме SuperSpeed ​​под Windows 8

    Возможно ли запланировать мой ноутбук для отключения звука и включения звука в определенное время?

    Какая ошибка: 1; исправить в CSS?

    Использование переменной iteratorа цикла foreach в выражении lambda – почему не удается?

    Как настроить SkyDrive, чтобы просто хранить мои файлы и не синхронизировать их?

    Как установить различное приложение для каждой комбинации вкуса с использованием flavorDimensions?

    Разница между заголовками Content-Range и Range?

    Давайте будем гением компьютера.