.net -内存泄漏剖析;

Translate

在.NET透视图中:

  • 什么是内存泄漏?
  • 您如何确定您的应用程序是否泄漏?有什么影响?
  • 如何防止内存泄漏?
  • 如果您的应用程序存在内存泄漏,则在进程退出或被杀死时会消失吗?还是即使进程完成后,应用程序中的内存泄漏也会影响系统上的其他进程吗?
  • 通过COM Interop和/或P / Invoke访问的非托管代码又如何呢?
This question and all comments follow the "Attribution Required."

所有的回答

Translate

我见过的最好的解释是免费的第7章编程电子书的基础.

基本上,在。净当引用的对象植根时发生内存泄漏,因此无法进行垃圾回收。当您保留超出预期范围的引用时,会意外发生。

您将知道开始获取OutOfMemoryExceptions时发生泄漏,或者内存使用量超出了您的预期(性能监视器有不错的内存计数器)。

理解。净的内存模型是避免这种情况的最佳方法。具体而言,了解垃圾收集器的工作方式和引用的工作方式-再次,请您参考电子书的第7章。另外,请注意常见的陷阱,可能是最常见的事件。如果对象A注册到对象上的事件B,然后反对A会一直呆到物体B消失是因为B持有对A。解决方案是在完成后注销事件。

当然,良好的内存配置文件将使您可以查看对象图并探索对象的嵌套/引用,以查看引用来自何处以及由哪个根对象负责(红门蚂蚁档案,JetBrains dotMemory,内存分析器是非常好的选择,或者您可以使用纯文字WinDbg紧急求救,但除非您是真正的专家,否则我强烈建议您使用商业/视觉产品)。

我相信非托管代码会受到其典型的内存泄漏的影响,只是共享引用由垃圾收集器管理。最后一点我可能是错的。

来源
Translate

严格来说,内存泄漏正在消耗程序“不再使用”的内存。

“不再使用”的含义不只一种,可能意味着“不再引用”,即完全不可恢复,或者可能意味着已引用,可恢复,未使用,但程序仍会保留引用。只有后者适用于.Net完美管理的对象。但是,并非所有类都是完美的,在某个时刻,底层的非托管实现可能会永久泄漏该进程的资源。

在所有情况下,应用程序消耗的内存都比严格需要的多。副作用(取决于泄漏的数量)可能从无到由过度收集导致的放慢,到一系列内存异常,最后是致命错误,然后强制终止进程。

当监视显示越来越多的内存分配给您的进程时,您知道应用程序存在内存问题在每个垃圾收集周期之后。在这种情况下,您要么在内存中保留了太多内存,要么某些底层的非托管实现正在泄漏。

对于大多数泄漏,资源是在进程终止时恢复的,但是在某些精确的情况下,某些资源并非总是可以恢复的,因此GDI游标句柄是众所周知的。当然,如果您具有进程间通信机制,则在该进程释放或终止该进程之前,不会释放在其他进程中分配的内存。

来源
Translate

我认为“什么是内存泄漏”和“什么是影响”问题已经很好地回答了,但是我想在其他问题上再添加一些内容...

如何了解您的应用程序是否泄漏

一种有趣的方式是打开性能并添加跟踪所有堆中的#个字节#Gen 2收藏,在每种情况下都只看您的过程。如果行使一项特定功能导致总字节数增加,并且该内存在下一个Gen 2收集之后仍保持分配状态,则您可能会说该功能泄漏了内存。

如何预防

还给出了其他好的意见。我只想补充一点,也许最常被忽视.NET内存泄漏的原因是将事件处理程序添加到对象而不删除它们。附加到对象的事件处理程序是对该对象的一种引用形式,因此即使在所有其他引用都消失之后,它也会阻止收集。始终记得分离事件处理程序(使用-=C#中的语法)。

当进程退出时,泄漏会消失吗?COM互操作又如何呢?

当您的进程退出时,操作系统将回收映射到其地址空间的所有内存,包括从DLL提供服务的任何COM对象。比较少的是,COM对象可以从单独的进程提供服务。在这种情况下,当您的进程退出时,您可能仍要负责所使用的任何COM服务器进程中分配的内存。

来源
Translate

我将内存泄漏定义为一个对象,该对象在完成后不会释放所有分配的内存。我发现如果您在框架和第三方组件中使用Windows API和COM(即其中包含错误或未正确管理的非托管代码),则可能在您的应用程序中发生这种情况。我还发现在使用某些物体(例如笔)后不整理可能会引起问题。

我个人遭受了内存不足异常的影响,这可能是由于内存异常引起的,但并非仅因.net应用程序中的内存泄漏而引起。 (OOM也可以来自固定查看固定艺术品)。如果您没有收到OOM错误,或者需要确认是否是导致此问题的内存泄漏,那么唯一的方法就是分析应用程序。

我还将尝试确保以下几点:

a)实现Idisposable的所有内容都可以通过使用finally块或using语句(包括画笔,钢笔等)进行处置(有些人争辩说,将所有内容都设置为空)

b)具有find方法的所有内容均使用finally或using语句再次关闭(尽管我发现using并不总是关闭,具体取决于您是否在using语句之外声明了对象)

c)如果您使用的是非托管代码/ Windows API,则这些代码/窗口API将在之后正确处理。 (有些有清理方法来释放资源)

希望这可以帮助。

来源
Translate

如果您需要诊断.NET中的内存泄漏,请检查以下链接:

http://msdn.microsoft.com/zh-CN/magazine/cc163833.aspx

http://msdn.microsoft.com/zh-CN/magazine/cc164138.aspx

这些文章描述了如何创建进程的内存转储以及如何对其进行分析,以便您可以首先确定泄漏是不受管理的还是受管理的,以及如果泄漏得到管理,如何确定泄漏的来源。

Microsoft还拥有一个更新的工具来帮助生成故障转储,以取代称为DebugDiag的ADPlus。

http://www.microsoft.com/downloads/details.aspx?FamilyID=28bd5941-c458-46f1-b24d-f60151d875a3&displaylang=en

来源
Translate

使用Microsoft的CLR Profilerhttp://www.microsoft.com/downloads/details.aspx?familyid=86ce6052-d7f4-4aeb-9b7a-94635beebdda&displaylang=en这是确定哪些对象正在保存内存,哪种执行流程导致创建这些对象以及监视哪些对象位于堆中何处(碎片,LOH等)的好方法。

来源
Translate

关于垃圾收集器如何工作的最好解释是杰夫·里希特斯(Jeff Richters)通过C#进行CLR书(第20章)。读这篇文章为理解对象如何持久化提供了很好的基础。

导致对象意外扎根的最常见原因之一是通过连接超出类的事件。如果您挂了一个外部事件

例如

SomeExternalClass.Changed += new EventHandler(HandleIt);

并在处理时忘记解开它,那么SomeExternalClass会引用您的类。

如上所述,SciTech内存分析器擅长向您显示怀疑泄漏的物体的根源。

但是,还有一种非常快速的方法来检查特定类型,即只需使用WnDBG(甚至可以在连接时在VS.NET即时窗口中使用它):

.loadby sos mscorwks
!dumpheap -stat -type <TypeName>

现在,做一些您认为会处理该类型对象的事情(例如,关闭窗口)。在这里有一个可以运行的调试按钮很方便System.GC.Collect()一些时间。

然后跑!dumpheap -stat -type <TypeName>再次。如果这个数字没有下降,或者没有下降到您期望的水平,那么您就有了进行进一步调查的基础。 (我从由英戈·拉默(Ingo Rammer)).

来源
Translate

我猜在托管环境中,泄漏是您对周围的大量内存保留了不必要的引用。

来源
Translate

人们为什么认为.NET中的内存泄漏与其他任何泄漏都不相同?

内存泄漏是当您附加到资源并且不让它消失时。您可以在托管和非托管编码中执行此操作。

关于.NET和其他编程工具,已经有关于垃圾收集的想法,以及其他使情况最小化的方法,这些情况会使您的应用程序泄漏。但是,防止内存泄漏的最佳方法是,您需要了解所使用平台上的基础内存模型以及其工作方式。

相信GC和其他魔术可以清除混乱,这是内存泄漏的一种捷径,以后很难找到。

在对非托管代码进行编码时,通常会确保清理,您知道所拥有的资源将是清理的责任,而不是看门人的责任。

另一方面,在.NET中,许多人认为GC将清除所有内容。好吧,它为您做了一些,但是您需要确保是这样。 .NET确实包装了很多东西,因此您并不总是知道您要处理的是托管资源还是非托管资源,因此需要确定要处理的内容。处理字体,GDI资源,活动目录,数据库等通常是您需要注意的事情。

用管理的术语讲,一旦进程被终止/删除,它确实会消失。

我看到很多人都这样做了,我真的希望这会结束。您不能要求用户终止您的应用程序以清理混乱!看一下可以是IE,FF等的浏览器,然后打开例如Google Reader,让它停留几天,然后看看会发生什么。

如果然后在浏览器中打开另一个选项卡,浏览到某个站点,然后关闭承载导致浏览器泄漏的另一个页面的选项卡,您是否认为浏览器会释放内存? IE并非如此。如果我使用Google阅读器,则IE可以在很短的时间内(大约3-4天)轻松占用1 GiB的内存。一些新闻页甚至更糟。

来源
Translate

我猜在托管环境中,泄漏是您对周围的大量内存保留了不必要的引用。

绝对。另外,在适当的情况下不对一次性对象使用.Dispose()方法可能会导致内存泄漏。最简单的方法是使用using块,因为它最后自动执行.Dispose():

StreamReader sr;
using(sr = new StreamReader("somefile.txt"))
{
    //do some stuff
}

并且,如果您创建的类使用的是非托管对象,那么如果您未正确实现IDisposable,则可能导致类用户的内存泄漏。

来源
Translate

通过程序终止解决所有内存泄漏。

内存不足,操作系统可能会决定代表您解决问题。

来源
Pat
Translate

对于.net中的内存泄漏,我将与Bernard保持一致。

您可以分析应用程序以查看其内存使用情况,并确定是否在不应该使用的情况下管理大量内存,则可以说它存在泄漏。

用管理的术语讲,一旦进程被终止/删除,它确实会消失。

非托管代码本身就是野兽,如果其中存在泄漏,它将遵循标准的内存。泄漏定义。

来源
Translate

还要记住,.NET有两个堆,一个是大对象堆。我相信大约85k或更大的对象放在此堆上。该堆的生存规则与常规堆不同。

如果您要创建大型内存结构(词典或列表的内存),则应谨慎查找确切的规则。

至于在进程终止时回收内存,除非您运行的是Win98或同等版本,否则一切都会在终止时释放回OS。唯一的例外是跨进程打开的事物,而另一个进程仍具有打开的资源。

COM对象可能很棘手。如果您始终使用IDispose模式,您会很安全。但是我遇到了一些实现的互操作程序集IDispose。这里的关键是打电话Marshal.ReleaseCOMObject完成后。 COM对象仍然使用标准的COM参考计数。

来源
Translate

我发现.Net内存探查器在.Net中发现内存泄漏时提供了非常好的帮助。它不是像Microsoft CLR Profiler一样免费,但是我认为它更快,更重要。一种

来源
Translate

一种定义是:无法释放无法访问的内存,无法在分配过程执行期间将其分配给新进程。大多数情况下,可以使用GC技术对其进行固化,也可以通过自动化工具进行检测。

有关更多信息,请访问http://all-about-java-and-weblogic-server.blogspot.in/2014/01/what-is-memory-leak-in-java.html.

来源