代码之家  ›  专栏  ›  技术社区  ›  huseyint

解剖“记忆泄漏”

  •  164
  • huseyint  · 技术社区  · 18 年前

    在.NET透视图中:

    • 什么是 memory leak ?
    • 如何确定应用程序是否泄漏?有什么影响?
    • 如何防止内存泄漏?
    • 如果您的应用程序有内存泄漏,那么当进程退出或被终止时,它会消失吗?或者应用程序中的内存泄漏是否会影响系统上的其他进程,甚至在进程完成之后?
    • 那么通过COM互操作和/或P/Invoke访问的非托管代码呢?
    15 回复  |  直到 8 年前
        1
  •  109
  •   q-l-p Felix Kling    8 年前

    我看到的最好的解释是在《自由》第七章 Foundations of Programming e-book .

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

    当你开始脱离内存的时候,你就会知道你有漏洞,异常或者你的内存使用量超出了你的预期。( 性能监视器 有很好的内存计数器)。

    理解 .NET 记忆模型是避免这种情况的最好方法。具体来说,了解垃圾收集器是如何工作的,以及引用是如何再次工作的,我将向您介绍电子书的第7章。此外,要注意常见的陷阱,可能最常见的是事件。中频对象 注册到对象上的事件 然后对象 会一直呆到物体 消失是因为 保留对的引用 . 解决方案是在完成后注销事件。

    当然,一个好的内存配置文件可以让您看到对象图,并探索对象的嵌套/引用,以查看引用来自何处以及根对象负责什么。( red-gate ants profile ,JetBrains点记忆, memprofiler 是很好的选择,或者只能使用文本 风铃 紧急求救信号 但我强烈推荐商业/视觉产品,除非你是真正的大师)。

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

        2
  •  33
  •   Coincoin    18 年前

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

    “不再使用”的含义不止一个,它可能意味着“不再引用它”,也就是说,完全不可恢复,或者它可能意味着引用、可恢复、未使用,但程序仍然保留这些引用。只有后者适用于.NET 完美管理的对象 . 然而,并不是所有的类都是完美的,并且在某个时刻,底层的非托管实现可能会永久地泄漏该进程的资源。

    在所有情况下,应用程序消耗的内存都比严格需要的内存多。根据泄漏的内存数量,副作用可能从“无”变为过度收集导致的速度减慢,变为一系列内存异常,最后是一个致命错误,然后强制终止进程。

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

    对于大多数泄漏,资源在进程终止时被恢复,但是在某些精确的情况下,有些资源并不总是被恢复,因此gdi光标句柄是出了名的。当然,如果您有进程间通信机制,那么在另一个进程释放或终止之前,不会释放在该进程中分配的内存。

        3
  •  29
  •   Brian Mains    14 年前

    我认为“什么是记忆泄漏”和“什么是效果”问题已经得到了很好的回答,但我想在其他问题上增加一些东西…

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

    一个有趣的方法是打开 性能监控 并添加跟踪 #所有堆中的字节数 #第2代收藏品 ,在每种情况下,只需查看您的流程。如果使用某个特定功能会导致总字节数增加,并且在下一代2收集之后该内存仍被分配,那么您可能会说该功能会泄漏内存。

    如何预防

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

    当进程退出时泄漏是否会消失,那么COM互操作呢?

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

        4
  •  19
  •   John    18 年前

    我将把内存泄漏定义为一个对象,它不会释放完成后分配的所有内存。我发现,如果您在框架和第三方组件中使用Windows API和COM(即在其中有错误或管理不正确的非托管代码),应用程序中可能会发生这种情况。我还发现,在使用笔之类的东西后没有整理会导致问题的发生。

    我个人经历过内存不足的异常,这些异常可能会导致,但并不排除在DOT NET应用程序中发生内存泄漏。(动臂也可以来自固定,参见 Pinning Artical )如果没有收到OOM错误,或者需要确认是否是内存泄漏导致的,那么唯一的方法就是分析应用程序。

    我也会努力确保:

    a)实现IDisposable的所有内容都可以使用finally块或using语句进行处理,这些语句包括画笔、笔等(有些人认为将所有内容都设置为Nothing addition)。

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

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

    希望这有帮助。

        5
  •  19
  •   vard thanhnguyen    10 年前

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

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

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

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

    微软也有一个更新的工具来帮助生成崩溃转储,替换adplus,称为debugdiag。

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

        6
  •  15
  •   Nick    18 年前

    使用来自Microsoft的CLR探查器 http://www.microsoft.com/downloads/details.aspx?familyid=86ce6052-d7f4-4aeb-9b7a-94635beebdda&displaylang=en 这是一种确定哪些对象正在保存内存、什么执行流导致创建这些对象以及监控哪些对象在堆中的位置(碎片、LOH等)的好方法。

        7
  •  15
  •   vard thanhnguyen    10 年前

    垃圾收集器如何工作的最好解释是Jeff Richters。 CLR via C# 书,(第20章)。阅读本文为理解对象如何持久提供了一个很好的基础。

    意外地生根对象的最常见原因之一是将事件挂接在类之外。如果你连接了一个外部事件

    例如

    SomeExternalClass.Changed += new EventHandler(HandleIt);
    

    并且在释放时忘记解开它,然后某个ExternalClass对您的类有一个引用。

    如上所述, SciTech memory profiler 非常擅长向你展示你怀疑泄漏的物体的根源。

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

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

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

    然后运行 !dumpheap -stat -type <TypeName> 再一次。如果这个数字没有下降,或者下降幅度没有你想象的那么大,那么你就有了进一步调查的基础。 (我从一个由 Ingo Rammer )

        8
  •  14
  •   Bernard    18 年前

    我想在一个受管理的环境中,泄漏可能是您对周围的大量内存保留了一个不必要的引用。

        9
  •  11
  •   Nathan Tuggy TonyLuigiC    9 年前

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

    内存泄漏是指当您连接到一个资源而不让它离开时。在托管和非托管编码中都可以这样做。

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

    相信GC和其他魔法可以清理你的烂摊子,这是记忆泄漏的捷径,以后很难找到。

    在编写非托管代码时,通常要确保清理,你知道你掌握的资源将是你清理的责任,而不是清洁工的责任。

    另一方面,在.NET中,很多人认为GC将清除所有内容。好吧,这对你有帮助,但你需要确保它是这样的。.NET确实包含很多东西,因此您并不总是知道您是否在处理托管或非托管资源,并且您需要确保处理的是什么。处理字体、GDI资源、Active Directory、数据库等通常是您需要注意的事情。

    从管理的角度来说,我会全力以赴 说它确实消失了一次 进程被终止/删除。

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

    如果您随后在浏览器中打开另一个选项卡,浏览到某个站点,然后关闭承载另一个导致浏览器泄漏的页面的选项卡,您认为浏览器会释放内存吗?在我的电脑上,如果我使用谷歌阅读器,IE很容易在短时间内(大约3-4天)吃掉1 Gib内存。有些新闻页面甚至更糟。

        10
  •  10
  •   Seibar    18 年前

    我想在一个有管理的环境中, 泄漏是你保留了一个 不必要的引用大块 周围的记忆。

    当然。此外,如果不在可释放对象上适当地使用.dispose()方法,可能会导致MEM泄漏。最简单的方法是使用using块,因为它在末尾自动执行.dispose():

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

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

        11
  •  10
  •   Siyual Ranjit Singh    10 年前

    我会同意伯纳德在.NET中的观点,即MEM泄漏是什么。

    您可以对应用程序进行概要分析,以查看其内存使用情况,并确定如果它在管理大量内存时不应该出现这种情况,您可以说它有一个漏洞。

    在管理方面,我会把我的脖子放在线上说,一旦进程被取消/删除,它就会消失。

    非托管代码是它自己的野兽,如果其中存在泄漏,它将遵循标准MEM。泄漏定义。

        12
  •  9
  •   Josh    18 年前

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

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

        13
  •  7
  •   Brian Mains    14 年前

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

    如果您正在创建大型内存结构(字典或列表),那么应该谨慎地查找准确的规则。

    就在进程终止时回收内存而言,除非运行win98或其等效程序,否则终止时所有内容都会释放回操作系统。唯一的例外是跨进程打开的东西,而另一个进程仍然打开资源。

    COM对象可能很棘手。如果你总是使用 IDispose 模式,你会安全的。但我已经运行了一些实现 IDISSAT . 这里的钥匙是打电话 Marshal.ReleaseCOMObject 完成后。COM对象仍然使用标准的COM引用计数。

        14
  •  6
  •   Lars Truijens    18 年前

    我发现 .Net Memory Profiler 在.NET中查找内存泄漏时提供了非常好的帮助。它不像微软的CLR分析器那样免费,但在我看来,它越来越快。一

        15
  •  1
  •   J0e3gan jfriend00    11 年前

    一个定义是: 无法释放无法访问的内存,在执行分配进程期间,无法再将其分配给新进程。它主要可以通过使用GC技术或自动工具检测来治愈。

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

    推荐文章