Is it possible to have a memory leak in managed co

2019-01-26 05:52发布

For instance if I have a hierarchical data structure:

class Node
{
    public List<Node> children;
}

and it is populated to many levels down then in one of the parents go:

myNode.children.Clear();

which will clear all the references to the immediate children - but how about all the grand children, grand grand children etc. that were referenced by those immediate children? Is C# clever enough to know they are no longer needed and they will be garbage collected?

I have read using WPF data binding without implementing interface INotifyChanged can cause memory leaks: http://blogs.msdn.com/b/micmcd/archive/2008/03/07/avoiding-a-wpf-memory-leak-with-databinding-black-magic.aspx, how is that possible in a managed environment?

11条回答
爷、活的狠高调
2楼-- · 2019-01-26 06:25

A memory leak is basically a piece of memory that is no longer required for the proper behavior of a program but cannot be freed due to a programming mistake. So the concept of memory leaks has nothing to do with Garbage Collection, C#, or Java.

Take this example:

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

Note how elements in the list are memory leaks because they are referenced by variables a1 ... an

This is just a simple example of why it is not just up to C# to take care of memory leaks. It is also the responsibility of the developer to fix this:

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

This will tell the C# garbage collector that all these elements should be collected.

查看更多
来,给爷笑一个
3楼-- · 2019-01-26 06:31

The garbage collector only collects objects that are no longer used - memory leaks are caused by objects still holding references to objects even though they shouldn't.

In your case, if a grand children is used by another object, then .Clear will remove it from the List of Nodes, but the garbage collector will not collect it. It will collect all other grand children though.

Example:

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楼-- · 2019-01-26 06:38

Yes, the garbage collector will work out that the grandchildren etc are garbage. Basically, if there's no way of getting to an object, it's considered garbage and eligible for collection.

As for how memory "leaks" are possible in managed code - it's typically if you end up with an object which is reachable via object references, but where there's no way you can end up "clearing" those references via an API.

That's the case in the blog post you quoted:

There is an issue where WPF checks to find things that implement INotifyProperyChanged. If there is a databinding to something not implementing this interface, then it makes a record in a global table. That record doesn't get cleaned up, as WPF has no way of checking when that DB record is no longer needed.

So there's this global table maintaining references, and you have no way of indicating that an item in the table can be cleared.

查看更多
smile是对你的礼貌
5楼-- · 2019-01-26 06:38

As an aside, you can also get memory leaks in .net if you use the unsafe keyword. If you use pointers in the same fashion as c++ etc and aren't careful to ensure you don't "loose" a pointer reference then the GC wouldn't be able to collect it.

example of unsafe block;

unsafe
{
int * ptr1, ptr2;
ptr1 = &var1;
ptr2 = ptr1;
*ptr2 = 20;
}
查看更多
我命由我不由天
6楼-- · 2019-01-26 06:40

Sure, with C# especially short of the other reference allocation things everyone has talked about, if you have a class that wraps native resources but it's never disposed of (or you lose the reference to it), you can create a leak.

Here is an example from the Image class:

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 is disposable, so since I'm disposing of the image at the end there shouldn't be a leak right? Actually, the fact that you overwrite the reference each time with a new image, means you can't dispose of the underlying GDI+ resources that the old image reference held. This will introduce a memory leak.

Since the gc doesn't call dispose for you and the Image class doesn't override the Finalize method (and call Dispose there), then you've got yourself a leak.

查看更多
ゆ 、 Hurt°
7楼-- · 2019-01-26 06:41

Yes, you can have a whole graph of objects (massive data structure) but if none of it is tied down or referred to, it will be garbage collected.

If it's not, either the GC hasn't run (you could try a GC.Collect() for diagnostic purposes but you shouldn't use it in production code) or something is referring to a part of the structure. For example, the UI might be bound to it.

查看更多
登录 后发表回答