托管代码中是否可能存在内存泄漏?(特别是C#3.0)

mar*_*mnl 12 .net c# memory-leaks

例如,如果我有一个分层数据结构:

class Node
{
    public List<Node> children;
}
Run Code Online (Sandbox Code Playgroud)

然后在其中一个父母那里填充到很多级别:

myNode.children.Clear();
Run Code Online (Sandbox Code Playgroud)

这将清除所有对直系孩子的提及 - 但那些直系孩子所引用的所有大孩子,大孙子等等呢?C#是否足够聪明,知道它们不再需要它们会被垃圾收集?

我已阅读使用WPF数据绑定而没有实现接口INotifyChanged可能导致内存泄漏:http://blogs.msdn.com/b/micmcd/archive/2008/03/07/avoiding-a-wpf-memory-leak-with-数据绑定-black-magic.aspx,在托管环境中如何实现?

Jon*_*eet 14

是的,垃圾收集器会发现孙子等是垃圾.基本上,如果没有办法到达某个对象,它就被认为是垃圾并且有资格收集.

至于如何记忆"泄露"是在托管代码中可能的-这是典型的,如果你结束了一个对象,它通过对象引用到达的,但是那里没有办法,你可以最终通过API"清理"那些引用.

在你引用的博客文章中就是这种情况:

WPF检查是否存在实现INotifyProperyChanged的问题.如果存在对未实现此接口的数据绑定,则它在全局表中创建记录.该记录未被清除,因为WPF无法检查何时不再需要该DB记录.

因此,这个全局表维护引用,并且您无法指示可以清除表中的项.

  • 我认为我们应该使用术语对象泄漏,其中对象存在引用但无法清除的情况.使用术语"内存泄漏"意味着GC以某种方式被破坏,这导致了误解和类似这样的问题. (4认同)

Cod*_*aos 6

C#并不关心.这是CLR完成GC的工作.

GC从已知的根对象(静态字段,局部变量,...)开始,并遍历引用,直到找到所有可到达的对象.可以收集所有其他对象(不包括一些终结器相关的东西).

因此,如果子引用实际上是对这些对象的唯一引用,那么也将收集大孩子.但是如果一些活着的外部对象仍然具有对您的一个节点的引用,则该节点和它引用的所有其他对象将保持活动状态.


托管内存泄漏是由保持对象存活的引用引起的.

例如,当使用数据库时,GUI具有对象的引用,使它们保持活动状态.

类似地,订阅事件会使与事件处理程序关联的对象保持活动状态.所以有时事件使用弱引用来避免这个问题.


Mic*_*tum 6

垃圾收集器只收集不再使用的对象 - 内存泄漏是由仍然持有对象引用的对象引起的,即使它们不应该.

在你的情况下,如果一个大孩子被另一个对象使用,那么.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.
    }
}
Run Code Online (Sandbox Code Playgroud)