“内存泄漏”剖析

在.NET的angular度来看:

  • 什么是内存泄漏
  • 你如何确定你的应用程序是否泄漏? 有什么影响?
  • 你怎么能防止内存泄漏?
  • 如果您的应用程序有内存泄漏,当进程退出或死亡时它会消失吗? 或者即使在进程完成后,应用程序中的内存泄漏也会影响系统上的其他进程?
  • 那么通过COM Interop和/或P / Invoke访问非托pipe代码呢?

我自己对这些问题有一些答案,但是它们不完整。 你怎么看?

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

基本上,在.NET中,当被引用的对象被植入时会发生内存泄漏,因此不能被垃圾收集。 当您坚持超出预期范围的引用时,会发生意外。

你会知道,当你开始忘记内存时,或者你的内存使用量超出了你的预期(perfmon有很好的内存计数器),就会发生泄漏。

了解.NET的内存模型是避免它的最好方法。 具体来说,了解垃圾收集器的工作原理以及参考如何工作(再次,我将把您引向电子书的第7章)。 另外,要注意常见的陷阱,可能是最常见的事件。 如果对象A注册到对象B上的事件,则对象A将继续存在,直到对象B消失,因为B持有对A的引用。解决scheme是在完成时取消注册事件。

当然,一个好的内存configuration文件可以让你看到你的对象图,并探索你的对象的嵌套/引用,以查看引用来自哪里,根对象是负责任的( 红门antconfiguration文件 ,JetBrains dotMemory, memprofiler真的很好select,或者你可以使用纯文本windbg和sos,但我强烈推荐一个商业/视觉产品,除非你是一个真正的大师)。

我相信非托pipe代码是典型的内存泄漏unamanged代码,除了两者之间共享的引用由垃圾收集器pipe理。 这最后一点可能是错的。

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

“不再使用”的含义不止一个,可能意味着“不再提及”,也就是完全不可恢复的,也可能是指被引用的,可恢复的,未被使用的,但程序仍然保留引用。 只有后者适用于完美pipe理对象的 .Net。 然而,并不是所有的类都是完美的,并且在某个时候,底层的非托pipe实现可能会永久性地泄露资源。

在所有情况下,应用程序消耗的内存都比严格需要的要多。 双方的影响,取决于泄露的数量,可能从无到有,过度收集造成的放缓,一系列的内存exception,最后是一个致命的错误,之后是强制进程终止。

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

对于大多数泄漏,当进程终止时资源被恢复,但是在某些精确的情况下,一些资源并不总是被恢复,GDI游标句柄是臭名昭着的。 当然,如果你有一个进程间通信机制,在另一个进程中分配的内存不会被释放,直到该进程释放或终止。

我认为“什么是内存泄漏”和“有什么影响”这个问题已经得到了很好的回答,但是我想在其他问题上增加更多的内容。

如何理解你的应用程序是否泄漏

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

如何预防

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

当进程退出时泄漏是否消失,COM互操作如何?

当你的进程退出时,所有映射到其地址空间的内存都被操作系统回收,包括从DLL提供的任何COM对象。 相对而言很less,可以从不同的进程提供COM对象。 在这种情况下,当您的进程退出时,您可能仍然负责在您使用的任何COM服务器进程中分配的内存。

我将定义内存泄漏作为一个对象,没有释放分配完成后的所有内存。 我发现如果您正在使用Windows API和COM(即在其中存在错误或未被正确pipe理的非托pipe代码),在框架和第三方组件中,您的应用程序中可能会发生这种情况。 我也发现在使用某些像笔这样的物体后可能会导致问题。

我个人遭受了内存exception,这可能是由于networking应用程序中的内存泄漏造成的,但并不是唯一的。 (OOM也可以来自钉住看Pinning Artical )。 如果你没有得到OOM错误,或者需要确认是否是内存泄漏,那么唯一的办法就是分析你的应用程序。

我也会尝试并确保以下内容:

a)实现Idisposable的所有东西都是使用finally块或使用语句(包括画笔,钢笔等)来处置的(有些人认为把所有东西都设置为无)

b)任何具有close方法的东西都会使用finally或using语句再次closures(尽pipe如果您在using语句之外声明该对象,我发现使用并不总是closures)

c)如果你正在使用非托pipe代码/ Windows API,这些处理后正确。 (一些有清理方法释放资源)

希望这可以帮助。

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

http://msdn.microsoft.com/en-us/magazine/cc163833.aspx

http://msdn.microsoft.com/en-us/magazine/cc164138.aspx

这些文章描述了如何创buildstream程的内存转储以及如何分析它,以便您可以首先确定您的泄漏是不受pipe理或pipe理的,以及是否pipe理,如何确定其来源。

微软还有一个更新的工具来帮助生成崩溃转储,以取代名为DebugDiag的ADPlus。

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

我想在一个托pipe的环境中,泄漏将是你不必要的引用大量的内存。

垃圾收集器如何工作的最佳解释是通过C#书籍Jeff Richters CLR (第20章)。 阅读这篇文章为理解物体如何坚持提供了很好的基础。

意外生根对象最常见的原因之一就是把事件拖到课堂外。 如果你连接一个外部事件

例如

 SomeExternalClass.Changed += new EventHandler(HandleIt); 

忘了解开它,然后SomeExternalClass有你的类的引用。

如上所述, SciTech内存分析器非常适合向您展示您怀疑存在泄漏的对象的根源。

但也有一个非常快速的方法来检查一个特定types只是使用WnDBG(你甚至可以在VS.NET立即窗口中使用这个附件):

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

现在做一些你认为会处理这种types的对象(例如closures一个窗口)。 在这里有一个debuggingbutton,可以运行System.GC.Collect()几次。

然后再次运行!dumpheap -stat -type <TypeName> 。 如果这个数字没有下降,或者没有达到预期的那么多,那么你有进一步调查的基础。 (我从Ingo Rammer的研讨会得到了这个build议)。

我想在一个托pipe的环境中,泄漏将是你不必要的引用大量的内存。

绝对。 此外,在适当的时候不使用可丢弃对象上的.Dispose()方法会导致内存泄漏。 最简单的方法是使用块,因为它会自动执行.Dispose()结尾:

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

如果你创build一个使用非托pipe对象的类,如果你没有正确地实现IDisposable,你可能会导致你的类的用户的内存泄漏。

为什么人们认为.NET中的内存泄漏与其他泄漏不一样?

内存泄漏是当你连接到一个资源,不要让它走。 您可以在托pipe编码和非托pipe编码中执行此操作。

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

相信GC和其他魔法会清理你的烂摊子是内存泄漏的简短方法,并且以后很难find。

编码不受pipe理时,通常要确保清理,你知道你掌握的资源将是你清理的责任,而不是清理员的责任。

另一方面,在.NET中,很多人都认为GC会清理所有的东西。 那么,它为你做了一些,但你需要确保它是如此。 .NET包装了许多东西,所以你并不总是知道你是在处理一个托pipe的还是非托pipe的资源,你需要确定你在处理什么。 处理字体,GDI资源,活动目录,数据库等是你需要注意的事情。

在pipe理方面,我会把我的脖子放在线上,说一旦这个过程被杀死或被删除,它就会消失。

我看到很多人都有这个,我真的希望这会结束。 你不能要求用户终止你的应用程序来清理你的烂摊子! 看一下浏览器,可以是IE,FF等,然后打开Goog​​le Reader,让它停留几天,看看会发生什么。

如果您在浏览器中打开另一个选项卡,请浏览某个网站,然后closures托pipe导致浏览器泄漏的其他页面的选项卡,您认为浏览器会释放内存吗? 与IE不同。 在我的电脑上,如果使用Google Reader,IE会在短时间内(大约3-4天)轻松地吃掉1GB的内存。 有些新闻报道更糟。

所有的内存泄漏都是通过程序终止来解决的。

泄漏足够的内存,操作系统可能会决定以您的名义解决问题。

我会同意伯纳德在.net中的内存泄漏是什么。

你可以分析你的应用程序,看看它的内存使用情况,并确定如果它pipe理大量的内存,当它不应该是你可以说它有泄漏。

在pipe理方面,我会把我的脖子放在线上,说一旦这个过程被杀死或被删除,它就会消失。

非托pipe代码是它自己的野兽,如果它内部存在泄漏,它将遵循标准的内存。 泄漏定义。

另外请记住,.NET有两个堆,一个是大对象堆。 我相信大约85k或更大的物体被放在这个堆上。 这个堆有一个不同于普通堆的生命周期规则。

如果你正在创build大的内存结构(字典或列表),谨慎地去查找确切的规则是什么。

至于回收进程终止的内存,除非你运行的是Win98或者它的等价物,否则一切都会在终止时被释放回操作系统。 唯一的例外是跨进程打开的事件,另一个进程仍然打开资源。

COM对象可能会很棘手。 如果你总是使用IDispose模式,你会很安全。 但是我已经运行了一些实现IDispose互操作程序集。 这里的关键是在完成之后调用Marshal.ReleaseCOMObject 。 COM对象仍然使用标准的COM引用计数。

我发现.Net Memory Profiler在.Net中查找内存泄漏时非常有帮助。 它不像Microsoft CLR Profiler那样免费,但在我看来,速度更快,更重要。 一个

一个定义是: 无法释放无法访问的内存,在执行分配过程中不能再分配给新进程。 它主要可以通过使用气相色谱技术或通过自动化工具检测到。

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