Утечки памяти модулей

У меня есть приложение, в котором присутствует много утечек памяти. Например, если открыть представление и закрыть его в 10 раз, мое потребление памяти растет, поскольку взгляды не полностью очищены. Это мои утечки памяти. С точки зрения testdriven я хотел бы написать тест, подтверждающий мои утечки и (после того, как я исправил утечку), утверждая, что я его исправил. Таким образом, мой код не будет разбит позже. Короче говоря:

Есть ли способ утверждать, что мой код не пропускает память из unit test?

например. Могу ли я сделать что-то вроде этого:

objectsThatShouldNotBeThereCount = MemAssertion.GetObjects<MyView>().Count;
Assert.AreEqual(0, objectsThatShouldNotBeThereCount);

Меня не интересует профилирование. Я использую Antic Profiler (который мне очень нравится), но также хотел бы написать тесты, чтобы убедиться, что "утечки" не возвращаются

Я использую С#/Nunit, но меня интересует любой, кто имеет философию в этом...

Ответ 1

Увеличение объема памяти не обязательно является признаком утечки ресурсов, поскольку сбор мусора не является детерминированным и, возможно, еще не ударил. Несмотря на то, что вы "отпускаете" объекты, CLR может свободно их поддерживать до тех пор, пока он считает, что в системе доступно достаточно ресурсов.

Если вы знаете, что у вас действительно есть утечка ресурсов, вы можете работать с объектами, которые имеют явное Close/Dispose как часть их контракта (предназначенных для "использования..." ). В этом случае, если у вас есть контроль над типами, вы можете отметить удаление объектов из своей реализации Dispose, чтобы убедиться, что они были фактически удалены, если вы можете жить с управлением жизненным циклом, протекающим в интерфейсе типа.

Если вы сделаете последнее, можно unit test заключить договорную утилизацию. Я делал это в некоторых случаях, используя эквивалент приложения для IDisposable (расширение этого интерфейса), добавляя параметр для запроса, был ли объект удален. Если вы реализуете этот интерфейс явно на свой тип, он не будет загрязнять его интерфейс.

Если у вас нет контроля над рассматриваемыми типами, профайлер памяти, как было предложено в другом месте, является инструментом, который вам нужен. (Например dotTrace от Jetbrains.)

Ответ 2

Вам не нужны модульные тесты, в которых вам нужен профайлер памяти. Вы можете начать с CLR Profiler.

Ответ 3

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

Классическим примером этого является System.Threading.Timer, который принимает метод обратного вызова в качестве параметра. Поскольку таймер в конечном счете использует неуправляемый ресурс, вводится новый корень GC, который может быть освобожден только путем вызова метода таймера Dispose. В этом случае ваш тип также должен реализовывать IDisposable, иначе этот объект никогда не сможет быть собранным мусором (утечка).

Вы можете написать unit test для этого сценария, выполнив что-то похожее на это:

        var instance = new MyType();

        // ...
        // Use your instance in all the ways that may trigger creation of new GC roots
        // ...

        var weakRef = new WeakReference(instance);

        instance.Dispose();
        instance = null;

        GC.Collect();
        GC.WaitForPendingFinalizers();
        GC.Collect();
        Assert.IsFalse(weakRef.IsAlive);

Ответ 4

dotMemory Unit имеет возможности программно проверять количество определенных объектов, трафик памяти, делать и сравнивать снимки памяти.

Ответ 5

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

Как создаются объекты? Непосредственно или каким-то способом, которым можно управлять. Если управляемые возвращают расширенные версии с финализаторами, которые регистрируют, что они были удалены. Тогда

GC.Collect();
GC.WaitForPendingFinalizers();
Assert.IsTrue(HasAllOfTypeXBeenFinalized());

Ответ 6

Как насчет чего-то типа:

long originalByteCount = GC.GetTotalMemory(true);
SomeOperationThatMayLeakMemory();
long finalByteCount = GC.GetTotalMemory(true);
Assert.AreEqual(originalByteCount, finalByteCount);