Почему .NET не может иметь утечки памяти? - PullRequest
57 голосов
/ 26 марта 2010

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

Насколько я понимаю, сам фреймворк написан на C ++, а C ++ подвержен утечкам памяти.

  • Является ли базовый фреймворк настолько хорошо написанным, что у него абсолютно нет возможности утечки внутренней памяти?
  • Есть ли в коде фреймворка что-то, что самостоятельно управляет и даже устраняет свои потенциальные утечки памяти?
  • Является ли ответ чем-то еще, что я не учел?

Ответы [ 16 ]

94 голосов
/ 26 марта 2010

.NET может иметь утечки памяти.

В основном люди обращаются к сборщику мусора, который решает, когда можно избавиться от объекта (или всего цикла объектов). Это позволяет избежать утечек памяти в стиле classic c и c ++, под которыми я подразумеваю выделение памяти и не освобождение ее позже.

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

Это обычно тот случай, когда события регистрируются (с +=), но не регистрируются позже, но также и при доступе к неуправляемому коду (используя pInvokes или объекты, которые используют базовые системные ресурсы, такие как соединения файловой системы или базы данных) и неправильно распоряжаться ресурсами.

43 голосов
/ 27 марта 2010

Здесь уже есть несколько хороших ответов, но я хочу затронуть еще один момент. Давайте снова внимательно посмотрим на ваш конкретный вопрос:


Насколько я понимаю, сам фреймворк написан на C ++, а C ++ подвержен утечкам памяти.

  • Является ли базовый фреймворк настолько хорошо написанным, что у него абсолютно нет возможности утечки внутренней памяти?
  • Есть ли в коде фреймворка что-то, что самостоятельно управляет и даже устраняет свои потенциальные утечки памяти?
  • Является ли ответ чем-то еще, что я не учел?

Ключом здесь является различие между вашим кодом и их кодом. Инфраструктура .Net (а также Java, Go, python и другие языки, собираемые мусором) обещают, что если вы будете использовать их код, ваш код не утечет память ... в хотя бы в традиционном смысле. Вы можете оказаться в ситуациях, когда некоторые объекты не освобождаются, как вы ожидаете, но эти случаи слегка отличаются от традиционных утечек памяти, поскольку объекты все еще доступны в вашей программе.

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

Так что теперь вы должны спросить себя: вы бы предпочли доверять своему коду или их коду? Имейте в виду, что их код не только проверен первоначальными разработчиками (точно так же, как ваши, не так ли?), Но и защищен от ежедневного использования тысячами (возможно, миллионами) других программистов, таких как вы. Любые существенные проблемы утечки памяти будут в числе первых выявлены и исправлены. Опять же, я не говорю, что это невозможно. Просто лучше доверять их коду, чем себе ... по крайней мере, в этом отношении.

Поэтому правильный ответ здесь - это вариант вашего первого предложения:

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

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

15 голосов
/ 26 марта 2010

После просмотра документации Microsoft, в частности « Определение утечек памяти в CLR », Microsoft заявляет, что до тех пор, пока вы не внедрите небезопасный код в вашем приложении, невозможно получить утечка памяти

Теперь они также указывают на концепцию предполагаемой утечки памяти или, как было отмечено в комментариях, «утечки ресурсов», которая представляет собой использование объекта, который имеет длительные ссылки и не утилизируется должным образом. Это может произойти с объектами ввода-вывода, объектами DataSet, элементами графического интерфейса и т.п. Это то, что я обычно приравниваю к «утечке памяти» при работе с .NET, но они не являются утечками в традиционном смысле.

12 голосов
/ 26 марта 2010

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

редактировать

Лучший пример подлинной утечки, который я видел до сих пор, - это обработчик событий + = ошибка.

1012 * редактировать *

См. Ниже объяснение ошибки и условий, при которых она квалифицируется как подлинная утечка, а не почти подлинная утечка.

4 голосов
/ 31 марта 2010

Вот другие утечки памяти, которые этот парень обнаружил, используя ANTS .NET Profiler: http://www.simple -talk.com / dotnet / .net-tools / tracing-memory-leaks-in-.net-apps-with -ants-профайлер /

4 голосов
/ 26 марта 2010

Вот пример утечки памяти в .NET, которая не связана с unsafe / pinvoke и даже не включает обработчики событий.

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

class Message 
{
  public Message(int id, string text) { MessageId = id; Text = text; }
  public int MessageId { get; private set; }
  public string Text { get; private set; }
}

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

Итак, вы добавляете новое свойство ...

class Message
{
  ...
  public Message PreviousMessage { get; private set; }
  ...
}

И вы пишете код для его установки. И, конечно, где-то в главном цикле вы должны иметь переменную, чтобы не отставать от последнего сообщения:

  Message lastMessageReceived;

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

3 голосов
/ 26 марта 2010

Полагаю, можно писать программное обеспечение, например, среда выполнения .NET (CLR), которая не пропускает память, если вы достаточно осторожны. Но поскольку Microsoft время от времени выпускает обновления для .NET Framework через Центр обновления Windows, я вполне уверен, что случайные ошибки даже в CLR.

Все программное обеспечение может вытекать из памяти.

Но, как уже отмечали другие, существуют другие виды утечек памяти. Хотя сборщик мусора устраняет «классические» утечки памяти, по-прежнему существует, например, проблема освобождения так называемых неуправляемых ресурсов (таких как подключения к базе данных, открытые файлы, элементы графического интерфейса и т. Д.). Вот где появляется интерфейс IDisposable.

Кроме того, недавно я столкнулся с возможной утечкой памяти при настройке .NET-COM interop . Компоненты COM используют количество ссылок, чтобы решить, когда они могут быть освобождены. .NET добавляет еще один механизм подсчета ссылок, на который может влиять статический класс System.Runtime.InteropServices.Marshal.

В конце концов, вам все равно нужно быть осторожным с управлением ресурсами, даже в программе .NET.

2 голосов
/ 27 марта 2010

Вы можете иметь утечки памяти в коде .NET. Некоторые объекты, в некоторых случаях, сами получают права root (хотя обычно это IDisposable). В этом случае невозможность вызова Dispose() для объекта приведет к реальной утечке памяти в стиле C / C ++ с выделенным объектом, на который у вас нет возможности ссылаться.

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

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

Вот пример кода с использованием System.Threading.Timer.

public class Test
{
    static public int Main(string[] args)
    {
        MakeFoo();
        GC.Collect();
        GC.Collect();
        GC.Collect();
        System.Console.ReadKey();
        return 0;
    }

    private static void MakeFoo()
    {
        Leaker l = new Leaker();
    }
}

internal class Leaker
{
    private Timer t;
    public Leaker()
    {
        t = new Timer(callback);
        t.Change(1000, 0);
    }

    private void callback(object state)
    {
        System.Console.WriteLine("Still alive!");
        t.Change(1000, 0);
    }
}

Так же, как GlaDOS , объект Leaker будет бесконечно "все еще жив" - однако, нет никакого способа получить доступ к объекту (кроме как изнутри, и как объект может узнать, когда на него не ссылаются больше?)

1 голос
/ 01 апреля 2012

Эта ссылка показывает, как утечки могут происходить в .Net при использовании слабых шаблонов событий. http://msdn.microsoft.com/en-us/library/aa970850.aspx

1 голос
/ 03 августа 2010

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

...