Возможно ли иметь утечку памяти в управляемом коде? (в частности, С# 3.0)

Например, если у меня есть иерархическая структура данных:

class Node
{
    public List<Node> children;
}

и он заполнен многими уровнями вниз, а затем у одного из родителей:

myNode.children.Clear();

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

Я прочитал с использованием привязки данных WPF без реализации интерфейса. INotifyChanged может вызвать утечку памяти: http://blogs.msdn.com/b/micmcd/archive/2008/03/07/avoiding-a-wpf-memory-leak-with-databinding-black-magic.aspx, как это возможно в управляемой среде

Ответ 1

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

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

В этом случае в сообщении в блоге вы цитируете:

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

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

Ответ 2

С# все равно. Это задача CLR для выполнения GC.

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

Итак, если ссылки на ребенка действительно являются единственными ссылками на эти объекты, тогда также будут собраны великие дети. Но если какой-то живой внешний объект все еще имеет ссылку на один из ваших узлов, этот node и все остальные объекты, на которые он ссылается, будут сохранены.


Утечки управляемой памяти вызваны ссылками, в которых объекты сохраняются.

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

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

Ответ 3

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

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

Пример:

class Foo {
 public Node SomeProperty {get; set;}

    public void SomeFunction(){
        var node = new Node { children = new List<Node>() };
        var childNode = new Node();
        var childNode2 = new Node();
        node.children.Add(childNode);
        node.children.Add(childNode2);
        SomeProperty = childNode2;

        node.children.Clear();
        // childNode will be garbage collected
        // childNode2 is still used by SomeProperty,
        // so it won't be garbage collected until SomeProperty or the instance
        // of Foo is no longer used.
    }
}

Ответ 4

Как и в стороне, вы можете также получить утечки памяти в .net, если используете ключевое слово unsafe. Если вы используете указатели так же, как С++ и т.д., И не будете осторожны, чтобы убедиться, что вы не "потеряете" ссылку на указатель, GC не сможет ее собрать.

пример небезопасного блока;

unsafe
{
int * ptr1, ptr2;
ptr1 = &var1;
ptr2 = ptr1;
*ptr2 = 20;
}

Ответ 5

Конечно, с С# особенно не хватает других ссылок на ссылки, о которых все говорили, если у вас есть класс, который обертывает собственные ресурсы, но он никогда не удаляется (или вы теряете ссылку на него), вы можете создать утечку.

Вот пример из класса Image:

public static void MemLeak()
{
    var src = @"C:\users\devshorts\desktop\bigImage.jpg";

    Image image1 = null;

    foreach (var i in Enumerable.Range(0, 10))
    {
        image1 = Image.FromFile(src);
    }

    image1.Dispose();

    Console.ReadLine();
}

Image является одноразовым, так что, поскольку я удаляю изображение в конце, не должно быть прямой утечки? Фактически, тот факт, что вы перезаписываете ссылку каждый раз новым изображением, означает, что вы не можете избавиться от базовых ресурсов GDI +, которые хранятся в старой ссылке. Это приведет к утечке памяти.

Поскольку gc не вызывает dispose для вас, а класс Image не переопределяет метод Finalize (и вызывает Dispose там), тогда у вас есть утечка.

Ответ 6

Да, у вас может быть целый граф объектов (массивная структура данных), но если ни один из них не связан или не упоминается, он будет собираться мусором.

Если это не так, либо GC не запускается (вы можете попробовать GC.Collect() для диагностики, но вы не должны использовать его в производственном коде), или что-то относится к части структуры. Например, пользовательский интерфейс может быть привязан к нему.

Ответ 7

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

То, что может привести к утечкам в методе, однако, - это объекты, которые случайно ссылаются на статические элементы, например.

Ваш пример попадает в первую категорию и поэтому безопасен в использовании.

Ответ 8

В .NET возможно иметь утечку памяти.

Если у вас есть объект "A", который регистрируется в событии на другом объекте "B" , тогда "B" получает ссылку на "A" и будет продолжаться, если вы не отмените регистрацию события, когда "A" выходит из сферы действия. В этом случае "А" не может быть собрано мусором, так как все еще есть активная ссылка. Он будет придерживаться, пока "B" не будет собрано мусором.

Если у вас есть ситуация, когда объекты "A" создаются и выходят за пределы области действия, вы получите все больше "A" в памяти.

Ответ 9

Я бы посоветовал узнать, как обрабатывается сбор мусора в мире .net. В настоящее время он работает по следующим ссылкам, чтобы найти что-нибудь, на что можно ссылаться на объект верхнего уровня, и освобождает все остальное; он не работает с деструкторами, такими как мир С++, поэтому вы можете быть счастливы в том, что управляемые объекты будут "просто идти", если их родитель и grand-parent (s) будут освобождены.

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

Сложный бит возникает при работе с тем, что может ссылаться на объект, и оно включает в себя некоторые менее очевидные вещи, такие как обработчики событий, в которых упоминается проблема с WPF/INotifyPropertyChanged.

Ответ 10

Утечка памяти - это в основном кусок памяти, который больше не требуется для правильного поведения программы, но не может быть освобожден из-за ошибки программирования. Таким образом, концепция утечек памяти не имеет ничего общего с Garbage Collection, С# или Java.

Возьмем этот пример:

var list = new List<Node>();
Node a1 = new Node();
Node a2 = new Node();
// ...
Node an = new Node();

// Populate list
list.Add(a1);
list.Add(a2);
// ...
list.Add(an);

// use this list
DoStuffTo(list);

// clear list -- release all elements
list.Clear();

// memory leaks from now on

Обратите внимание, что элементы в списке являются утечками памяти, поскольку на них ссылаются переменные a1 ... an

Это простой пример того, почему С# не только заботится о утечке памяти. Разработчик также несет ответственность за это:

// Clear references
a1 = null;
a2 = null;
// ...
an = null;

Это сообщит сборщику мусора С#, что все эти элементы должны быть собраны.

Ответ 11

Да, утечки в С# вызываются, когда ссылки на объекты не удаляются должным образом после того, как эти объекты больше не нужны. Если ссылка на объект была удалена, тогда объект будет освобожден Сборщиком мусора при его запуске (он автоматически устанавливается в зависимости от времени, определяемого тщательно настроенным алгоритмом, поэтому лучше не запускать его вручную, за исключением случаев, когда вы действительно знаете, что делаете!). Но если ссылка на объект не удалена должным образом, сборщик мусора по-прежнему считает, что приложение необходимо, поэтому происходит утечка памяти. Его особенно распространено, чтобы найти такого рода события с обработчиками событий, которые arent должным образом избавились. Если объект с дочерними/внуками имеет все ссылки на него, то этот объект, а также все эти дети/внуки также будут удалены при следующем запуске сборщика мусора (если на них не ссылаются и в другом месте).

Лучше всего использовать профайлер памяти, который позволит вам посмотреть, какие объекты хранят другие объекты в памяти (большинство из них позволяет делать снимки памяти, а затем смотреть на какой-то график, показывающий ссылки. существует, когда он не должен, вы можете посмотреть на график, показывающий, какая ссылка хранит этот объект в памяти, и использовать его для разработки, где вы должны были очистить ссылку, чтобы избежать утечки памяти. Существует несколько профилировщиков, но я нахожу муравьев профайлер памяти красными воротами проще всего использовать http://www.red-gate.com/products/dotnet-development/ants-memory-profiler/.