Подтвердить что ты не робот

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

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

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

4b9b3361

Ответ 1

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

MyType object2 = new MyType();

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

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

object2.Dispose();

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

Ответ 2

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

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;
    }
}

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

Ответ 3

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

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

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

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

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

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

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

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

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

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

Ответ 4

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

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

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

Ответ 5

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

Ответ 6

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

Ответ 7

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

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

Ответ 9

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

Ответ 10

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

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

Ответ 11

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

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

Ответ 12

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

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

Ответ 13

На самом деле это не утечка памяти, но при использовании больших объектов довольно просто запустить память (более 64 КБ, если я правильно помню). Они хранятся на LOH, и это НЕ дефрагментировано. Таким образом, использование этих больших объектов и освобождение их освобождает память на LOH, но эта свободная память больше не используется средой выполнения .NET для этого процесса. Таким образом, вы можете легко выбежать из космоса на LOH, используя только несколько больших объектов на LOH. Эта проблема известна Microsoft, но, как я помню, сейчас решение для этого планируется.

Ответ 14

Хотя возможно, что что-то в структуре имеет утечку, более вероятно, что у вас есть что-то, что не удаляется должным образом или что-то блокирует GC от его утилизации, IIS будет основным кандидатом для этого,

Просто помните, что не все в .NET - это полностью управляемый код, COM-взаимодействие, файловые файлы, такие как файловые потоки, запросы БД, изображения и т.д.

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

Ответ 15

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

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

Ответ 16

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

Мы делали много быстрых вставок данных в странной ситуации, когда соединение с базой данных нужно было открывать и закрывать каждый раз. Сторонняя библиотека сделала некоторые из тех же открытий, что мы должны были делать вручную и не документировали. В нем также содержались ссылки, которые мы так и не нашли. В результате было создано столько же соединений, сколько должно было быть, и только 1/2 закрывались. И поскольку ссылки были проведены, у нас была утечка памяти.

Это, очевидно, не то же самое, что классическая утечка памяти C/С++, но для всех целей и задач она была для нас одной.

Ответ 17

Если он считает утечку памяти, это может быть достигнуто и с помощью такого кода:

public class A
{
    B b;
    public A(B b) { this.b = b; }
    ~A()
    {
        b = new B();
    }
}

public class B
{
    A a;
    public B() { this.a = new A(this); }
    ~B()
    {
        a = new A(this);
    }
}

class Program
{
    static void Main(string[] args)
    {
        {
            B[] toBeLost = new B[100000000];
            foreach (var c in toBeLost)
            {
                toBeLost.ToString(); //to make JIT compiler run the instantiation above
            }
        }
        Console.ReadLine();
    }
}

Ответ 18

Маленькие функции помогают избежать "утечек памяти". Поскольку сборщик мусора освобождает локальные переменные в конце функций. Если функция большая и занимает много памяти, вы сами должны освобождать локальные переменные, которые занимают много памяти и больше не нужны. Аналогичные глобальные переменные (массивы, списки) также плохие.

У меня возникли утечки памяти на С# при создании изображений, а не их удалении. Это немного странно. Люди говорят, что вам нужно вызвать .Dispose() для каждого объекта, который имеет его. Но документация для графических функций С# не всегда упоминает об этом, например, для функции GetThumbnailImage(). Компилятор С# должен предупредить вас об этом, я думаю.

Ответ 19

Самопомощь Как найти утечку памяти:

  • Удалить и gc.collect вызовы.
  • Подождите, пока мы не убедимся в утечке памяти.
  • Создайте файл дампа из диспетчера задач.
  • Откройте файлы дампа, используя DebugDiag.
  • Сначала проанализируйте результат. Результат от нас должен помочь нам, который, как правило, занимает большую часть памяти.
  • Исправьте код, пока там не будет обнаружена утечка памяти.
  • Используйте стороннее приложение, такое как .net-профайлер. (Мы можем использовать пробную версию, но вам нужно решить проблему как можно скорее. Первый дамп должен помочь нам в основном о том, как происходит утечка)
  • Если проблема связана с виртуальной памятью, необходимо следить за неуправляемой памятью. (Обычно там есть другая конфигурация, которая должна быть включена)
  • Запустите стороннее приложение, основанное на его использовании.

Проблема с утечкой памяти:

  • События/делегаты никогда не удаляются. (При утилизации убедитесь, что событие не зарегистрировано) - см. ReepChopsey.
  • Список/Словарь никогда не удалялся.
  • Объект, ссылающийся на другой объект, который сохраняется в памяти, никогда не будет удален. (Клонирование для упрощения управления).