首页 文章

解读“记忆泄漏”

提问于
浏览
163

在.NET透视图中:

  • 什么是memory leak

  • 如何确定应用程序是否泄漏?有什么影响?

  • 如何防止内存泄漏?

  • 如果您的应用程序有内存泄漏,当进程退出或被杀死时它是否会消失?或者,即使在流程完成后,应用程序中的内存泄漏也会影响系统上的其他进程?

  • 那么通过COM Interop和/或P / Invoke访问的非托管代码呢?

15 回答

  • 6

    我见过的最好的解释是在免费Foundations of Programming e-book的第7章 .

    基本上,在 .NET 中,当引用的对象被植根时会发生内存泄漏,因此无法进行垃圾回收 . 当您坚持超出预期范围的引用时,会意外地发生这种情况 .

    你'll know that you have leaks when you start getting OutOfMemoryExceptions or your memory usage goes up beyond what you'期待( PerfMon 有很好的记忆计数器) .

    了解 .NET 的内存模型是避免它的最好方法 . 具体来说,了解垃圾收集器的工作原理以及引用的工作方式 - 再次,我将向您介绍电子书的第7章 . 另外,要注意常见的陷阱,可能是最常见的事件 . 如果对象 A 已注册到对象 B 上的事件,则对象 A 将一直存在,直到对象 B 消失,因为 B 持有对 A 的引用 . 解决方案是在完成后取消注册您的活动 .

    当然,一个好的内存配置文件可以让你看到你的对象图并探索你的对象的嵌套/引用,看看引用的来源和根对象的负责(red-gate ants profile,JetBrains dotMemory,memprofiler是非常好的选择,或者你可以使用纯文本 WinDbgSOS ,但我'd strongly recommend a commercial/visual product unless you'是一个真正的大师) .

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

  • 15

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

    "No longer used"有多个含义,它可能意味着"no more reference to it",即完全不可恢复,或者它可能意味着,引用,可恢复,未使用但程序仍保留引用 . 只有后者适用于.Net for perfectly managed objects . 但是,并非所有类都是完美的,并且在某些时候,潜在的非托管实现可能会永久地泄漏该进程的资源 .

    在所有情况下,应用程序消耗的内存超过严格需要的内存 . 根据泄漏的数量,边的影响可以从无,从过度收集导致的减速,到一系列内存异常,最后是强制错误,然后强制进程终止 .

    您知道当监视显示为您的进程 after each garbage collection cycle 分配了越来越多的内存时,应用程序会出现内存问题 . 在这种情况下,您要么在内存中保留太多,要么某些基础非托管实现正在泄漏 .

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

  • 1

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

    How to understand whether your application leaks

    一个有趣的方法是打开perfmon并为所有堆和#Gen 2集合中的#字节添加跟踪,在每种情况下只查看您的进程 . 如果执行特定功能导致总字节数增加,并且在下一代第2代集合后仍保留分配内存,则可能会说该功能会泄漏内存 .

    How to prevent

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

    Does the leak go away when the process exits, and what about COM interop?

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

  • 29

    我将内存泄漏定义为一个对象,它不会释放完成后分配的所有内存 . 我发现如果你在框架和第三方组件中使用Windows API和COM(即在其中存在错误或未正确管理的非托管代码),可能会在您的应用程序中发生这种情况 . 我也发现在使用钢笔之类的某些物体后可能会造成损坏问题 .

    我个人遭受了内存不足的异常,这可能是由于网络应用程序中的内存泄漏所致 . (OOM也可以来自固定看Pinning Artical) . 如果您没有收到OOM错误或需要确认是否是导致它的内存泄漏,那么唯一的方法是分析您的应用程序 .

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

    a)实现Idisposable的所有东西都是使用finally块或者using语句来处理的,这些包括画笔,笔等等(有些人认为除了之外什么都没有)

    b)使用finally或using语句再次关闭任何具有close方法的东西(尽管我发现using并不总是关闭,具体取决于你是否在using语句之外声明了对象)

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

    希望这可以帮助 .

  • 7

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

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

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

    这些文章描述了如何创建流程的内存转储以及如何对其进行分析,以便您可以首先确定泄漏是否未受管理或管理,以及如何管理,如何确定它的来源 .

    Microsoft还有一个更新的工具来帮助生成故障转储,以替换称为DebugDiag的ADPlus .

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

  • 9

    使用Microsoft http://www.microsoft.com/downloads/details.aspx?familyid=86ce6052-d7f4-4aeb-9b7a-94635beebdda&displaylang=en中的CLR Profiler是一种很好的方法,可以确定哪些对象占用内存,哪些执行流程可以创建这些对象,还可以监视哪些对象存在于堆上的哪些位置(碎片,LOH等) .

  • 19

    关于垃圾收集器如何工作的最佳解释是在Jeff Richters的书中,(第20章) . 阅读本文为理解对象如何持久提供了坚实的基础 .

    意外生根对象的最常见原因之一是将事件挂在课堂外 . 如果您挂钩外部事件

    例如

    SomeExternalClass.Changed += new EventHandler(HandleIt);
    

    当你处理时忘记取消它,然后SomeExternalClass有一个你的类的参考 .

    如上所述,SciTech memory profiler非常适合展示您怀疑泄漏的物体的根源 .

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

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

    现在做一些你认为会处理那种类型对象的东西(例如关闭一个窗口) . 这里有一个调试按钮可以很方便地运行 System.GC.Collect() 几次 .

    然后再次运行 !dumpheap -stat -type <TypeName> . 如果这个数字没有达到预期的水平,那么你就有了进一步调查的基础 . (我从Ingo Rammer的研讨会得到了这个提示) .

  • 19

    我想在托管环境中,泄漏就是你不必要地引用一大块内存 .

  • 15

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

    内存泄漏是指您附加到资源但不放弃它 . 您可以在托管和非托管编码中执行此操作 .

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

    相信GC和其他魔法会清理你的混乱是内存泄漏的简短方法,并且以后很难找到 .

    如果编码不受管理,您通常会确保清理,您知道您所掌握的资源将是您清理的责任,而不是清洁工 .

    另一方面,在.NET中,很多人认为GC会清理所有内容 . 嗯,它为你做了一些,但你需要确保它是如此 . .NET确实包含很多东西,所以你并不总是知道你是在处理托管资源还是非托管资源,而是需要确定你正在处理什么 . 处理字体,GDI资源,活动目录,数据库等通常是您需要注意的事项 .

    在管理方面,我会把我的脖子放在线上说它一旦杀死/移除过程就会消失 .

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

    如果您随后在浏览器中打开另一个选项卡,浏览某个站点,然后关闭托管导致浏览器泄漏的其他页面的选项卡,您认为浏览器是否会释放内存? IE不是这样 . 在我的电脑上,如果我使用谷歌阅读器,IE将在很短的时间内(大约3-4天)轻松吃掉1 GiB的内存 . 有些新闻报道甚至更糟 .

  • 10

    我想在一个托管环境中,泄漏就是你保持不必要的参考到了大块的记忆 .

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

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

    如果您创建一个使用非托管对象的类,如果您没有正确实现IDisposable,则可能会导致类的用户出现内存泄漏 .

  • 10

    我将同意伯纳德关于.net内存泄漏的内容 .

    您可以分析您的应用程序以查看其内存使用情况,并确定如果它不应该管理大量内存,您可以说它有泄漏 .

    在管理方面,我会把我的脖子放在线上,说它一旦杀死/移除过程就会消失 .

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

  • 11

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

    内存泄漏,操作系统可能会决定代表您解决问题 .

  • 33

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

    如果要创建大型内存结构(Dictionary或List),那么谨慎查找确切的规则是什么 .

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

    COM对象可能很棘手 . 如果您始终使用 IDispose 模式,则'll be safe. But I'已经运行了几个实现 IDispose 的互操作程序集 . 这里的关键是当你完成它时调用 Marshal.ReleaseCOMObject . COM对象仍然使用标准COM引用计数 .

  • 14

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

  • 109

    一个定义是: Unable to release unreachable memory, which can no longer be allocated to new process during execution of allocating process. It can mostly be cured by using GC techniques or detected by automated tools.

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

相关问题