“记忆泄漏”剖析

从.NET 的角度来看:

  • 什么是 内存泄漏
  • 如何确定应用程序是否泄漏? 其影响是什么?
  • 如何防止内存泄漏?
  • 如果您的应用程序存在内存泄漏,那么当进程退出或被终止时,内存泄漏消失了吗?或者应用程序中的内存泄漏是否会影响系统中的其他进程,即使在进程完成后也是如此?
  • 那么通过 COM 互操作和/或 P/Invoke 访问的非托管代码呢?
14875 次浏览

我猜想,在托管环境中,泄漏是指保留对大块内存的不必要引用。

我会同意伯纳德在.net 什么会是一个 mem 泄漏。

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

用管理术语来说,我将冒着生命危险说,一旦这个过程被终止/移除,它就会消失。

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

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

基本上,在 .NET中,当被引用的对象被根化时会发生内存泄漏,因此不能进行垃圾回收。当您持有超出预期范围的引用时,会偶然发生这种情况。

当您开始获得 OutOfMemory 异常或内存使用量超出预期时(PerfMon有很好的内存计数器) ,您将知道存在泄漏。

了解 .NET的内存模型是避免它的最佳方法。具体来说,就是理解垃圾收集器是如何工作的,以及引用是如何工作的ーー我再次提到电子书的第7章。另外,要注意常见的陷阱,可能最常见的就是事件。如果对象 A被注册到对象 B上的一个事件,那么对象 A将一直存在,直到对象 B消失,因为 B持有对 A的引用。解决方案是在完成后注销事件。

当然,一个好的内存配置文件可以让你看到你的对象图,探索嵌套/引用你的对象,看看引用来自哪里,哪个根对象负责(红门蚂蚁的轮廓,JetBrains dotMemory,Memprofiler是真正的好选择,或者你可以使用纯文本的 温德伯格求救,但我强烈推荐一个商业/视觉产品,除非你是一个真正的大师)。

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

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

“不再使用”有一个以上的意思,它可能意味着“没有更多的引用它”,即,完全不可恢复,或者它可能意味着,引用,可恢复,未使用,但程序保留引用无论如何。只有后者适用于。完美管理的对象净值。但是,并非所有类都是完美的,在某些时候,底层非托管实现可能会永久性地泄漏该流程的资源。

在所有情况下,应用程序都会消耗比严格需要的更多的内存。根据泄漏的数量,副作用可能从零开始,到由过度收集引起的减速,再到一系列内存异常,最后是一个致命错误,随后是强制进程终止。

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

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

我将内存泄漏定义为一个对象,在它完成后不释放所有分配的内存。我发现,如果您使用的是 Windows API 和 COM (即存在 bug 或未正确管理的非托管代码) ,那么在应用程序、框架和第三方组件中都可能发生这种情况。我还发现,使用某些物品(如钢笔)后不整理会导致这个问题。

我个人曾经遭受过内存不足异常,这可能导致,但不排他的内存泄漏点网络应用程序。(OOM 也可以来自固定参见 固定艺术品)。如果您没有收到 OOM 错误,或者需要确认是否是内存泄漏导致了这个错误,那么唯一的方法就是分析您的应用程序。

我还要努力确保以下几点:

A)实现我一次性的所有东西都是使用 finally 块或者 use 语句来处理的,这些语句包括画笔、钢笔等(有些人认为除此之外什么都不要设置)

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

C)如果你使用的是非托管代码/窗口 API,这些代码/窗口 API 会在。(有些有释放资源的清理方法)

希望这个能帮上忙。

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

如果内存泄漏过多,操作系统可能会决定代表您解决这个问题。

我想在有管理的环境中 泄露消息的人是你 对大块的不必要引用 回忆。

当然。另外,不使用。在适当的时候对一次性对象使用 Dispose ()方法可能会导致 mem 泄漏。最简单的方法是使用 using 块,因为它会自动执行。最后处理() :

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

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

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

如果您正在创建大型内存结构(Dictionary 或 List) ,那么谨慎的做法是查找确切的规则。

至于在进程终止时回收内存,除非您运行的是 Win98或其等价物,否则在终止时所有内存都会被释放回操作系统。唯一的例外是打开了跨进程的内容,而另一个进程仍然打开了资源。

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

如果您需要诊断.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

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

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

一种有趣的方法是打开 完美并添加 所有堆中的字节# 第二代收藏的跟踪,在每种情况下只查看您的进程。如果运行某个特定特性会导致总字节数增加,并且在下一个 Gen 2集合之后仍然分配该内存,那么可以说该特性会泄漏内存。

如何预防

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

进程退出时泄漏消失了吗? COM 互操作怎么办?

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

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

我发现 . Net Memory Profiler在寻找内存泄漏时提供了很好的帮助。网。它不像微软的 CLR Profiler 那样是免费的,但是在我看来它更快,也更切中要害。A

关于垃圾收集器如何工作的最佳解释是在 Jeff Richters 的 CLR via C # 书中(Ch。20).阅读这篇文章为理解对象如何持久化提供了很好的基础。

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

例如:。

SomeExternalClass.Changed += new EventHandler(HandleIt);

并且在释放时忘记解除它,那么 Some ExternalClass 就有一个对您的类的引用。

正如上面提到的,科技记忆分析仪非常擅长向您展示您怀疑正在泄漏的对象的根源。

但是也有一个检查特定类型的快速方法,那就是使用 WnDBG (你甚至可以在 VS.NET 的即时窗口中使用这个方法) :

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

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

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

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

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

关于。NET 和其他编程工具,已经有了垃圾收集的想法,以及其他方法来最大限度地减少将使您的应用程序泄漏的情况。 但是防止内存泄漏的最佳方法是您需要了解您所使用的平台上的底层内存模型以及它们是如何工作的。

相信 GC 和其他魔法可以清理您的烂摊子是内存泄漏的捷径,而且以后将很难找到。

当编写非托管代码时,您通常要确保清理,您知道您所掌握的资源将由您负责清理,而不是清洁工。

进去。另一方面,很多人认为 GC 会清理一切。这对你有好处,但你得确保如此。.NET 确实包装了很多东西,所以您并不总是知道您正在处理的是托管资源还是非托管资源,并且您需要确定您正在处理的是什么。处理字体、 GDI 资源、活动目录、数据库等通常是您需要注意的事情。

用管理术语来说,我会把我的脖子戴上 这句台词说,它确实消失了一次 过程被终止/删除。

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

如果你在浏览器中打开另一个选项卡,浏览某个网站,然后关闭承载导致浏览器泄漏的另一个页面的选项卡,你认为浏览器会释放内存吗?IE 就不一样了。在我的电脑上,如果我使用谷歌阅读器,IE 会在短时间内(大约3-4天)轻易地消耗掉1GiB 的内存。有些报纸甚至更糟。

一个定义是: 无法释放不可访问内存,在执行分配进程期间不能再将其分配给新进程。大多数情况下可以通过使用 GC 技术或通过自动化工具检测来治愈。

详情请浏览 http://all-about-java-and-weblogic-server.blogspot.in/2014/01/what-is-memory-leak-in-java.html