Модульное тестирование утечек памяти - PullRequest
14 голосов
/ 06 сентября 2010

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

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

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

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

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

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

Ответы [ 6 ]

5 голосов
/ 21 июля 2014

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

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

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

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);
5 голосов
/ 06 сентября 2010

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

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

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

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

2 голосов
/ 06 сентября 2010

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

1 голос
/ 06 июня 2016

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

0 голосов
/ 14 декабря 2010

Как насчет чего-то вроде:

long originalByteCount = GC.GetTotalMemory(true);
SomeOperationThatMayLeakMemory();
long finalByteCount = GC.GetTotalMemory(true);
Assert.AreEqual(originalByteCount, finalByteCount);
0 голосов
/ 06 сентября 2010

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

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

GC.Collect();
GC.WaitForPendingFinalizers();
Assert.IsTrue(HasAllOfTypeXBeenFinalized());
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...