Возможно ли иметь утечку памяти в управляемом коде?(в частности, C # 3.0) - PullRequest
12 голосов
/ 22 июня 2011

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

class Node
{
    public List<Node> children;
}

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

myNode.children.Clear();

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

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

Ответы [ 11 ]

14 голосов
/ 22 июня 2011

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

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

Это тот случай в цитируемом вами сообщении в блоге:

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

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

6 голосов
/ 22 июня 2011

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

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

Пример:

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.
    }
}
6 голосов
/ 22 июня 2011

C # не волнует.Задачей CLR является выполнение GC.

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

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


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

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

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

2 голосов
/ 24 апреля 2013

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

Вот пример из класса 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 там), то у вас есть утечка.

2 голосов
/ 22 июня 2011

Кроме того, вы можете также получить утечки памяти в .net, если используете ключевое слово unsafe .Если вы используете указатели так же, как c ++ и т. Д., И не уверены, что не «потеряете» ссылку на указатель, GC не сможет ее собрать.

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

unsafe
{
int * ptr1, ptr2;
ptr1 = &var1;
ptr2 = ptr1;
*ptr2 = 20;
}
0 голосов
/ 22 июня 2011

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

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

0 голосов
/ 22 июня 2011

Утечка памяти - это, по сути, часть памяти, которая больше не требуется для правильного поведения программы, но не может быть устранена из-за ошибки программирования. Таким образом, концепция утечек памяти не имеет ничего общего с сборкой мусора, C # или 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

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

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

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

0 голосов
/ 22 июня 2011

Я бы посоветовал прочитать о том, как сборка мусора обрабатывается в мире .net - по сути, он работает, следуя ссылкам, чтобы найти все, на что может ссылаться объект верхнего уровня, и освобождает всееще;он не работает с деструкторами, такими как мир C ++, поэтому вы можете быть счастливы, зная, что управляемые объекты «просто уйдут», если их родитель (ы) и дед (и) будут освобождены.

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

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

0 голосов
/ 22 июня 2011

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

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

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

0 голосов
/ 22 июня 2011

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

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

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

...