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

时间:2022-09-01 12:00:38

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?

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

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?

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

11 个解决方案

#1


14  

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.

至于托管代码中内存“泄漏”的可能性 - 通常情况下,如果您最终得到一个可通过对象引用访问的对象,但是您无法通过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.

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

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

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

#2


6  

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.

在你的情况下,如果一个大孩子被另一个对象使用,那么.Clear会将它从节点列表中删除,但垃圾收集器不会收集它。它会收集所有其他的大孩子。

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.
    }
}

#3


6  

C# does not care. The it's the CLRs job to do the GC.

C#并不关心。执行GC是CLR的工作。

The GC starts at known root objects(static fields, local variables,...) and walks the references until it has found all reachable objects. All other objects can be collected(excluding some finalizer related stuff).

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

So if the child references were really the only references to these objects then the grand children will be collected too. But if some alive outside object still has a reference to one of your nodes this node and all other objects referenced by it will be kept alive.

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


Managed memory leaks are caused by references which keep objects alive.

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

For example when using databining the GUI has references to the objects keeping them alive.

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

Similarly being subscribed to an event keeps the object associated with the event handler alive. So sometimes events use weak references to avoid this problem.

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

#4


2  

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.

另外,如果使用unsafe关键字,也可以在.net中获取内存泄漏。如果你以与c ++等相同的方式使用指针,并且不小心确保你没有“松散”指针引用,那么GC将无法收集它。

example of unsafe block;

不安全块的例子;

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

#5


2  

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.

当然,C#特别缺少每个人都讨论过的其他参考分配的东西,如果你有一个包装原生资源的类但它从未被丢弃(或者你失去对它的引用),你可以创建一个泄漏。

Here is an example from the Image class:

以下是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 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.

图像是一次性的,所以因为我在最后处理图像时不应该有泄漏吗?实际上,每次使用新图像覆盖引用这一事实意味着您无法处理旧图像引用所持有的基础GDI +资源。这将引入内存泄漏。

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.

由于gc没有为你调用dispose而Image类没有覆盖Finalize方法(并在那里调用Dispose),那么你自己就是泄密。

#6


0  

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.

如果不是,则GC未运行(您可以尝试使用GC.Collect()进行诊断,但不应在生产代码中使用它)或某些内容指的是结构的一部分。例如,UI可能绑定到它。

#7


0  

Circular references are no problem for the GC in .NET. It uses an algorithm to determine which objects are actually reachable from certain entry points (e.g. the main method).

循环引用对于.NET中的GC没有问题。它使用算法来确定哪些对象实际可从某些入口点到达(例如主方法)。

What can cause meory leaks, however, are objects which are accidentally referenced by static members for example.

然而,导致机械泄漏的是例如静态构件意外引用的物体。

Your example falls into the first category and is therefore safe to use.

您的示例属于第一类,因此可以安全使用。

#8


0  

It is possible to have a kind of memory leak in .NET.

在.NET中可能存在一种内存泄漏。

If you have an object "A" that registers to an event on another object "B", then "B" gets an reference to "A" and will continue to have so if you do not unregister the event when "A" gets out of scope. In this case "A" cannot be garbage collected as there is still an active reference. It will stick around until "B" is garbage collected.

如果您有一个对象“A”注册到另一个对象“B”上的事件,那么“B”将获得对“A”的引用,并且如果您在“A”退出时未取消注册该事件将继续这样做范围。在这种情况下,“A”不能被垃圾收集,因为仍然存在活动引用。它会一直存在,直到“B”被垃圾收集。

If you have a situation where "A" objects are created and goes out of scope continually you will get more and more "A"s in memory.

如果你有一个“A”对象被创建并且不断超出范围的情况,你将在内存中获得越来越多的“A”。

#9


0  

I'd suggest reading up on how garbage collection is handled in the .net world -- esentially, it works by following references to find anything that could be referenced by a top level object and frees everything else; it doesn't work with destructors like the C++ world, so you can be happy in the knowledge that managed objects will "just go" if their parent(s) and grand-parent(s) are freed.

我建议阅读.net世界中如何处理垃圾收集 - 实际上,它通过跟踪引用来查找可以被*对象引用的任何内容并释放其他所有东西;它不适用于像C ++这样的析构函数,因此如果托管对象将释放父对象和祖父对象,那么管理对象将“只是去”这些知识,你会感到高兴。

Of course, the garbage collector only knows about managed memory, and it is worth looking at the IDisposable pattern if you have any unmanaged resources - this allows deterministic release of non-managed objects.

当然,垃圾收集器只知道托管内存,如果你有任何非托管资源,值得查看IDisposable模式 - 这允许确定性释放非托管对象。

The complicated bit comes in when dealing with what could reference an object, and it does include some less obvious things, like event handlers, which is where the WPF/INotifyPropertyChanged issue you mentioned comes from.

在处理可以引用对象的内容时会出现一个复杂的位,它确实包含一些不那么明显的东西,比如事件处理程序,这就是你提到的WPF / INotifyPropertyChanged问题来自哪里。

#10


0  

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.

内存泄漏基本上是一段内存,不再需要程序的正常行为,但由于编程错误而无法释放。因此,内存泄漏的概念与垃圾收集,C#或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

请注意列表中的元素如何是内存泄漏,因为它们由变量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:

这只是一个简单的例子,说明为什么它不仅仅取决于C#来处理内存泄漏。开发人员还有责任解决此​​问题:

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

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

这将告诉C#垃圾收集器应该收集所有这些元素。

#11


0  

Yes, leaks in C# are caused when references to objects are not properly removed once those objects are no longer needed. If a reference to an object has been removed, then the object is got rid of by the Garbage Collector when it is run (it does this automatically based at times determined by a carefully tuned algorithm, so best not to manually cause it to run unless you really know what you’re doing!). But if the reference to the object isn’t properly removed, the Garbage Collector still thinks it is needed by the application, so the memory is leaked. It’s particularly common to find this sort of happening with event handlers which aren’t properly got rid of. If an object with children / grandchildren has all references to it removed, then that object as well as all those children / grandchildren will also be removed the next time the Garbage collector is run (unless they're also being referenced from elsewhere).

是的,一旦不再需要这些对象,就无法正确删除对象的引用,从而导致C#中的泄漏。如果删除了对象的引用,则垃圾收集器在运行时将其除去(它会根据经过仔细调整的算法自动执行此操作,因此最好不要手动使其运行,除非你真的知道你在做什么!)。但是如果没有正确删除对象的引用,垃圾收集器仍然认为应用程序需要它,因此内存泄漏。特别常见的是,事件处理程序发现这种情况并没有得到妥善解决。如果带有子/孙的对象删除了对它的所有引用,那么下次运行垃圾收集器时也会删除该对象以及所有这些子/孙子(除非它们也被从其他地方引用)。

The best thing is to use a memory profiler, which will let you look at what objects are holding other objects in memory (most let you take snapshots of memory then look at some kind of graph showing the references. If an object still exists when it shouldn’t, you can look at a graph showing what reference is holding that object in memory, and use that to work out where you should have cleared the reference to avoid the memory leaking. There are a few profilers available but I find ants memory profiler by red gate the easiest to use http://www.red-gate.com/products/dotnet-development/ants-memory-profiler/.

最好的方法是使用内存分析器,它可以让你查看哪些对象在内存中保存其他对象(大多数让你拍摄内存的快照,然后查看显示引用的某种图形。如果一个对象仍然存在,那么它不应该,你可以查看一个图表,显示在内存中保存该对象的引用,并使用它来计算应该清除引用的位置,以避免内存泄漏。有一些分析器可用,但我发现蚂蚁内存红门的分析器最容易使用http://www.red-gate.com/products/dotnet-development/ants-memory-profiler/。

#1


14  

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.

至于托管代码中内存“泄漏”的可能性 - 通常情况下,如果您最终得到一个可通过对象引用访问的对象,但是您无法通过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.

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

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

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

#2


6  

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.

在你的情况下,如果一个大孩子被另一个对象使用,那么.Clear会将它从节点列表中删除,但垃圾收集器不会收集它。它会收集所有其他的大孩子。

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.
    }
}

#3


6  

C# does not care. The it's the CLRs job to do the GC.

C#并不关心。执行GC是CLR的工作。

The GC starts at known root objects(static fields, local variables,...) and walks the references until it has found all reachable objects. All other objects can be collected(excluding some finalizer related stuff).

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

So if the child references were really the only references to these objects then the grand children will be collected too. But if some alive outside object still has a reference to one of your nodes this node and all other objects referenced by it will be kept alive.

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


Managed memory leaks are caused by references which keep objects alive.

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

For example when using databining the GUI has references to the objects keeping them alive.

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

Similarly being subscribed to an event keeps the object associated with the event handler alive. So sometimes events use weak references to avoid this problem.

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

#4


2  

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.

另外,如果使用unsafe关键字,也可以在.net中获取内存泄漏。如果你以与c ++等相同的方式使用指针,并且不小心确保你没有“松散”指针引用,那么GC将无法收集它。

example of unsafe block;

不安全块的例子;

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

#5


2  

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.

当然,C#特别缺少每个人都讨论过的其他参考分配的东西,如果你有一个包装原生资源的类但它从未被丢弃(或者你失去对它的引用),你可以创建一个泄漏。

Here is an example from the Image class:

以下是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 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.

图像是一次性的,所以因为我在最后处理图像时不应该有泄漏吗?实际上,每次使用新图像覆盖引用这一事实意味着您无法处理旧图像引用所持有的基础GDI +资源。这将引入内存泄漏。

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.

由于gc没有为你调用dispose而Image类没有覆盖Finalize方法(并在那里调用Dispose),那么你自己就是泄密。

#6


0  

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.

如果不是,则GC未运行(您可以尝试使用GC.Collect()进行诊断,但不应在生产代码中使用它)或某些内容指的是结构的一部分。例如,UI可能绑定到它。

#7


0  

Circular references are no problem for the GC in .NET. It uses an algorithm to determine which objects are actually reachable from certain entry points (e.g. the main method).

循环引用对于.NET中的GC没有问题。它使用算法来确定哪些对象实际可从某些入口点到达(例如主方法)。

What can cause meory leaks, however, are objects which are accidentally referenced by static members for example.

然而,导致机械泄漏的是例如静态构件意外引用的物体。

Your example falls into the first category and is therefore safe to use.

您的示例属于第一类,因此可以安全使用。

#8


0  

It is possible to have a kind of memory leak in .NET.

在.NET中可能存在一种内存泄漏。

If you have an object "A" that registers to an event on another object "B", then "B" gets an reference to "A" and will continue to have so if you do not unregister the event when "A" gets out of scope. In this case "A" cannot be garbage collected as there is still an active reference. It will stick around until "B" is garbage collected.

如果您有一个对象“A”注册到另一个对象“B”上的事件,那么“B”将获得对“A”的引用,并且如果您在“A”退出时未取消注册该事件将继续这样做范围。在这种情况下,“A”不能被垃圾收集,因为仍然存在活动引用。它会一直存在,直到“B”被垃圾收集。

If you have a situation where "A" objects are created and goes out of scope continually you will get more and more "A"s in memory.

如果你有一个“A”对象被创建并且不断超出范围的情况,你将在内存中获得越来越多的“A”。

#9


0  

I'd suggest reading up on how garbage collection is handled in the .net world -- esentially, it works by following references to find anything that could be referenced by a top level object and frees everything else; it doesn't work with destructors like the C++ world, so you can be happy in the knowledge that managed objects will "just go" if their parent(s) and grand-parent(s) are freed.

我建议阅读.net世界中如何处理垃圾收集 - 实际上,它通过跟踪引用来查找可以被*对象引用的任何内容并释放其他所有东西;它不适用于像C ++这样的析构函数,因此如果托管对象将释放父对象和祖父对象,那么管理对象将“只是去”这些知识,你会感到高兴。

Of course, the garbage collector only knows about managed memory, and it is worth looking at the IDisposable pattern if you have any unmanaged resources - this allows deterministic release of non-managed objects.

当然,垃圾收集器只知道托管内存,如果你有任何非托管资源,值得查看IDisposable模式 - 这允许确定性释放非托管对象。

The complicated bit comes in when dealing with what could reference an object, and it does include some less obvious things, like event handlers, which is where the WPF/INotifyPropertyChanged issue you mentioned comes from.

在处理可以引用对象的内容时会出现一个复杂的位,它确实包含一些不那么明显的东西,比如事件处理程序,这就是你提到的WPF / INotifyPropertyChanged问题来自哪里。

#10


0  

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.

内存泄漏基本上是一段内存,不再需要程序的正常行为,但由于编程错误而无法释放。因此,内存泄漏的概念与垃圾收集,C#或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

请注意列表中的元素如何是内存泄漏,因为它们由变量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:

这只是一个简单的例子,说明为什么它不仅仅取决于C#来处理内存泄漏。开发人员还有责任解决此​​问题:

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

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

这将告诉C#垃圾收集器应该收集所有这些元素。

#11


0  

Yes, leaks in C# are caused when references to objects are not properly removed once those objects are no longer needed. If a reference to an object has been removed, then the object is got rid of by the Garbage Collector when it is run (it does this automatically based at times determined by a carefully tuned algorithm, so best not to manually cause it to run unless you really know what you’re doing!). But if the reference to the object isn’t properly removed, the Garbage Collector still thinks it is needed by the application, so the memory is leaked. It’s particularly common to find this sort of happening with event handlers which aren’t properly got rid of. If an object with children / grandchildren has all references to it removed, then that object as well as all those children / grandchildren will also be removed the next time the Garbage collector is run (unless they're also being referenced from elsewhere).

是的,一旦不再需要这些对象,就无法正确删除对象的引用,从而导致C#中的泄漏。如果删除了对象的引用,则垃圾收集器在运行时将其除去(它会根据经过仔细调整的算法自动执行此操作,因此最好不要手动使其运行,除非你真的知道你在做什么!)。但是如果没有正确删除对象的引用,垃圾收集器仍然认为应用程序需要它,因此内存泄漏。特别常见的是,事件处理程序发现这种情况并没有得到妥善解决。如果带有子/孙的对象删除了对它的所有引用,那么下次运行垃圾收集器时也会删除该对象以及所有这些子/孙子(除非它们也被从其他地方引用)。

The best thing is to use a memory profiler, which will let you look at what objects are holding other objects in memory (most let you take snapshots of memory then look at some kind of graph showing the references. If an object still exists when it shouldn’t, you can look at a graph showing what reference is holding that object in memory, and use that to work out where you should have cleared the reference to avoid the memory leaking. There are a few profilers available but I find ants memory profiler by red gate the easiest to use http://www.red-gate.com/products/dotnet-development/ants-memory-profiler/.

最好的方法是使用内存分析器,它可以让你查看哪些对象在内存中保存其他对象(大多数让你拍摄内存的快照,然后查看显示引用的某种图形。如果一个对象仍然存在,那么它不应该,你可以查看一个图表,显示在内存中保存该对象的引用,并使用它来计算应该清除引用的位置,以避免内存泄漏。有一些分析器可用,但我发现蚂蚁内存红门的分析器最容易使用http://www.red-gate.com/products/dotnet-development/ants-memory-profiler/。