Утечка памяти в C # - PullRequest
       106

Утечка памяти в C #

54 голосов
/ 07 марта 2009

Возможно ли когда-либо в управляемой системе утечка памяти, когда вы убедитесь, что все ручки, вещи, которые реализуют IDispose, расположены?

Были бы случаи, когда некоторые переменные не учитывались?

Ответы [ 21 ]

63 голосов
/ 07 марта 2009

Обработчики событий - очень распространенный источник неочевидных утечек памяти. Если вы подписываетесь на событие на object1 из object2, затем выполняете object2.Dispose () и делаете вид, что его не существует (и удаляете все ссылки из вашего кода), в событии object1 есть неявная ссылка, которая не позволяет объекту2 мусор собрали.

MyType object2 = new MyType();

// ...
object1.SomeEvent += object2.myEventHandler;
// ...

// Should call this
// object1.SomeEvent -= object2.myEventHandler;

object2.Dispose();

Это распространенный случай утечки - забывание легко отписаться от событий. Конечно, если объект1 будет собран, объект2 также будет собран, но не раньше.

40 голосов
/ 07 марта 2009

Я не думаю, что утечки памяти в стиле C ++ возможны. Сборщик мусора должен учитывать это. Можно создать статический объект, который агрегирует ссылки на объекты, даже если эти объекты никогда не используются снова. Примерно так:

public static class SomethingFactory
{
    private static List<Something> listOfSomethings = new List<Something>();

    public static Something CreateSomething()
    {
        var something = new Something();
        listOfSomethings.Add(something);
        return something;
    }
}

Это явно глупый пример, но он будет эквивалентен утечке памяти во время выполнения.

27 голосов
/ 07 марта 2009

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

В .NET вы видите не утечки памяти, а объекты, которые никогда не удаляются. Объект не будет удален, пока сборщик мусора сможет найти его на графе объектов. Поэтому, если какой-либо живой объект где-либо имеет ссылку на объект, он не будет уничтожен.

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

Таким образом, вы должны следить за объектами, которые регистрируются для статических событий без вашего ведома. Например, отличная особенность ToolStrip заключается в том, что если вы измените тему отображения, она автоматически отобразится в новой теме. Это выполняет эту изящную особенность, регистрируясь для статического события SystemEvents.UserPreferenceChanged. Когда вы меняете тему Windows, событие вызывается, и все ToolStrip объекты, которые прослушивают событие, получают уведомление о появлении новой темы.

Хорошо, предположим, вы решили выбросить ToolStrip в вашей форме:

private void DiscardMyToolstrip()
{
    Controls.Remove("MyToolStrip");
}

Теперь у вас есть ToolStrip, который никогда не умрет. Даже если он больше не указан в вашей форме, каждый раз, когда пользователь меняет темы, Windows покорно сообщает об этом несуществующему ToolStrip. Каждый раз, когда запускается сборщик мусора, он думает: «Я не могу выбросить этот объект, событие UserPreferenceChanged использует его».

Это не утечка памяти. Но это может быть и так.

Подобные вещи делают профилировщик памяти бесценным. Запустите профилировщик памяти, и вы скажете: «Странно, кажется, в куче десять тысяч ToolStrip объектов, хотя в моей форме только один. Как это произошло?»

О, и если вам интересно, почему некоторые люди считают, что установщики свойств являются злыми: чтобы получить ToolStrip для отмены регистрации в событии UserPreferenceChanged, установите для его свойства Visible значение false.

20 голосов
/ 07 марта 2009

Делегаты могут привести к неинтуитивным утечкам памяти.

Всякий раз, когда вы создаете делегата из метода экземпляра, ссылка на этот экземпляр сохраняется "в" этом делегате.

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

17 голосов
/ 20 марта 2009

Если вы разрабатываете приложение WinForms, небольшая утечка - это свойство Control.AllowDrop (используется для включения перетаскивания). Если для AllowDrop установлено значение «true», CLR все равно будет удерживать ваш элемент управления, хотя System.Windows.Forms.DropTarget. Чтобы это исправить, убедитесь, что ваше Control свойство *1004* установлено в false, когда оно вам больше не нужно, а CLR позаботится обо всем остальном.

7 голосов
/ 07 марта 2009

Единственная причина утечки памяти в приложении .NET заключается в том, что на объекты все еще ссылаются, хотя срок их службы истек. Следовательно, сборщик мусора не может их собрать. И они становятся долгоживущими объектами.

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

7 голосов
/ 07 марта 2009

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

6 голосов
/ 03 ноября 2009
5 голосов
/ 07 марта 2009

Отражение излучения является еще одним потенциальным источником утечек, например, встроенные десериализаторы объектов и модные клиенты SOAP / XML. По крайней мере, в более ранних версиях платформы сгенерированный код в зависимых доменах приложений никогда не выгружался ...

4 голосов
/ 21 марта 2009

Это миф, что вы не можете утечь память в управляемом коде. Конечно, это намного сложнее, чем в неуправляемом C ++, но есть миллион способов сделать это. Статические объекты, содержащие ссылки, ненужные ссылки, кеширование и т. Д. Если вы делаете вещи «правильным» образом, многие из ваших объектов не будут собирать мусор намного позже, чем это необходимо, что, на мой взгляд, тоже является утечкой памяти практическим, а не теоретическим способом.

К счастью, есть инструменты, которые могут вам помочь. Я часто использую Microsoft CLR Profiler - это не самый удобный инструмент, когда-либо написанный, но он, безусловно, очень полезен и бесплатен.

...