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

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

Это - мое понимание, что сама платформа записана в C++, и C++ восприимчив к утечкам памяти.

  • Действительно ли базовая платформа так правильно написана, что она абсолютно не имеет никакой возможности утечек внутренней памяти?
  • Есть ли что-то в рамках кода платформы, который самосправляется и даже исправляет его собственные потенциальные утечки памяти?
  • Ответ - что-то еще, что я не рассмотрел?
57
задан Dinah 26 March 2010 в 19:17
поделиться

15 ответов

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


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

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

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

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

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

Следовательно, правильный ответ здесь состоит в том, что это вариант вашего первого предложения:

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

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

43
ответ дан 24 November 2019 в 19:14
поделиться

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

В книге Джеффри Рихтера CLR через C # есть хорошая глава о том, как управлять памятью в .NET.

0
ответ дан 24 November 2019 в 19:14
поделиться

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

У всего программного обеспечения может происходить утечка памяти.

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

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

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

3
ответ дан 24 November 2019 в 19:14
поделиться

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

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

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

Вот пример кода, использующего 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 будет бесконечно «все еще жив» - но нет никакого способа получить доступ к объекту (кроме внутреннего, и как объект может узнать когда на него больше не ссылаются?)

2
ответ дан 24 November 2019 в 19:14
поделиться

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

править

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

править

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

12
ответ дан 24 November 2019 в 19:14
поделиться

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

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

15
ответ дан 24 November 2019 в 19:14
поделиться

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

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

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

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

94
ответ дан 24 November 2019 в 19:14
поделиться

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

Но в .NET могут быть утечки памяти. Утечки GDI распространены, например, среди приложений Windows Forms. В одном из приложений, которое я помогал разрабатывать, это происходит регулярно. А когда сотрудники в офисе используют несколько его экземпляров в течение дня, нередко они достигают предела в 10 000 объектов GDI, присущего Windows.

1
ответ дан 24 November 2019 в 19:14
поделиться

Вот пример утечки памяти в .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;

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

4
ответ дан 24 November 2019 в 19:14
поделиться

Лучший пример, который я нашел, был на самом деле из Java, но тот же принцип применим и к C#.

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

Оказалось, что string.substring(...) сохраняет ссылку на исходную длинную строку... даже если мы сохранили только 1000 символов или около того, эти подстроки все равно использовали по несколько МБ памяти каждая. По сути, мы хранили в памяти содержимое каждого файла.

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

Редактирование: Не уверен, что эта конкретная проблема характерна для .NET. Идея заключалась в том, чтобы проиллюстрировать фактическую разработку/оптимизацию, выполненную в языке, собирающем мусор, которая в большинстве случаев была умной и полезной, но может привести к нежелательному использованию памяти.

0
ответ дан 24 November 2019 в 19:14
поделиться

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

1
ответ дан 24 November 2019 в 19:14
поделиться

Вот другие утечки памяти, которые этот парень нашел с помощью ANTS .NET Profiler: http://www.simple-talk.com/dotnet/.net-tools/tracing-memory-leaks-in-.net-applications-with-ants-profiler/

4
ответ дан 24 November 2019 в 19:14
поделиться

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

0
ответ дан 24 November 2019 в 19:14
поделиться

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

1
ответ дан 24 November 2019 в 19:14
поделиться

Один из основных источников утечек памяти C / C ++, которых фактически не существует в .Net, - это когда для освобождения разделяемой памяти

Следующее взято из курса Брэда Абрамса по проектированию библиотек классов .NET

«Ну, первый момент, конечно, нет утечек памяти, не так ли? Нет? Память все еще есть Утечки памяти? Есть другой вид утечки памяти. Как насчет этого? Утечки памяти, которых у нас не было, - это то, что в старом мире вы использовали некоторую память, а затем забываете освободить или добавьте ref и забудьте сделать релиз или что-то другое. И в новом мире сборщик мусора в конечном итоге владеет всей памятью, а сборщик мусора освободит этот материал, когда больше нет ссылок.Но утечки все же могут быть, не так ли? Какие бывают утечки? Что ж, если вы сохраните ссылку на этот объект, сборщик мусора не сможет это освободить. Очень часто случается, что вы думаете, что избавились от всего этого графа объектов, но один парень все еще держится за него со ссылкой, и тогда вы застреваете. Сборщик мусора не сможет освободить это, пока вы не удалите все ссылки на него.

Другой, я думаю, большая проблема. Нет проблем с владением памятью. Если вы прочитаете документацию по WIN32 API, вы увидите, хорошо, я сначала выделяю эту структуру и передаю ее, а затем вы заполняете ее, а затем я освобождаю ее. Или я говорю вам размер, и вы выделяете его, а потом я освобождаю его позже, или вы знаете, ведутся все эти дебаты о том, кому принадлежит эта память и где она должна быть освобождена. И часто разработчики просто отказываются от этого и говорят: «Хорошо, что угодно. Что ж, это будет бесплатно, когда приложение закроется », а это не такой уж хороший план.

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

Полная стенограмма

1
ответ дан 24 November 2019 в 19:14
поделиться
Другие вопросы по тегам:

Похожие вопросы: