在.NET透视图中:
答案 0 :(得分:110)
我见过的最好的解释是在免费Foundations of Programming e-book的第7章中。
基本上,在 .NET 中,当引用的对象被植根时会发生内存泄漏,因此无法进行垃圾回收。当您坚持超出预期范围的参考时,会意外地发生这种情况。
当你开始获得OutOfMemoryExceptions或你的内存使用量超出预期( PerfMon 有很好的内存计数器)时,你会知道你有泄漏。
了解 .NET 的内存模型是避免它的最好方法。具体来说,了解垃圾收集器的工作原理以及引用的工作方式 - 再次,我将您介绍到电子书的第7章。另外,要注意常见的陷阱,可能是最常见的事件。如果对象 A 已注册到对象 B 上的事件,则对象 A 将一直存在,直到对象 B 消失因为 B 包含对 A 的引用。解决方案是在完成后取消注册您的活动。
当然,一个好的内存配置文件可以让你看到你的对象图并探索你的对象的嵌套/引用,看看引用的来源和根对象的负责(red-gate ants profile,JetBrains dotMemory,{ {3}}是非常好的选择,或者您可以使用纯文本 WinDbg 和 SOS ,但我强烈推荐商业/视觉产品,除非您是真正的大师)。
我认为非托管代码受其典型内存泄漏的影响,但共享引用由垃圾收集器管理。我最后一点可能是错的。
答案 1 :(得分:35)
严格来说,内存泄漏正在消耗程序“不再使用”的内存。
“不再使用”具有多个含义,它可能意味着“不再引用它”,即完全不可恢复,或者它可能意味着,引用,可恢复,未使用但程序仍保留引用。只有后者适用于完美托管对象<。strong>的.Net。但是,并非所有类都是完美的,并且在某些时候,潜在的非托管实现可能会永久性地泄漏资源以进行该过程。
在所有情况下,应用程序消耗的内存超出严格要求。根据泄漏的数量,边的影响可以从无,从过度收集引起的减速,到一系列内存异常,最后是强制错误,然后强制进程终止。
您知道当监控显示在每个垃圾回收周期之后为您的进程分配了越来越多的内存时,应用程序会出现内存问题。在这种情况下,您要么在内存中保留太多,要么某些潜在的非托管实现正在泄漏。
对于大多数泄漏,在进程终止时恢复资源,但是在某些精确的情况下并不总是恢复某些资源,GDI游标句柄因此而臭名昭着。当然,如果您有进程间通信机制,则在该进程释放或终止之前,不会释放在另一进程中分配的内存。
答案 2 :(得分:32)
我认为“什么是内存泄漏”和“有什么影响”问题已经得到了很好的回答,但我想在其他问题上添加更多内容......
如何了解您的应用程序是否泄露
一个有趣的方法是打开 perfmon 并在所有堆和#Gen 2集合中添加#字节的跟踪,在每种情况下查找就在你的过程中。如果执行特定功能导致总字节数增加,并且在下一代第2代集合后仍保留分配内存,则可能会说该功能会泄漏内存。
如何预防
已经给出了其他好的意见。我只想补充一点,也许最常被忽视的 .NET内存泄漏的原因是将事件处理程序添加到对象而不删除它们。附加到对象的事件处理程序是对该对象的引用形式,因此即使在所有其他引用都已消失之后也会阻止收集。始终记得分离事件处理程序(使用C#中的-=
语法。)
进程退出时泄漏是否会消失,COM互操作会怎样?
当您的进程退出时,操作系统将回收映射到其地址空间的所有内存,包括从DLL提供的任何COM对象。相对很少,COM对象可以从单独的进程中提供。在这种情况下,当您的进程退出时,您可能仍然负责在您使用的任何COM服务器进程中分配的内存。
答案 3 :(得分:19)
我将内存泄漏定义为一个对象,它不会释放完成后分配的所有内存。我发现,如果您在框架和第三方组件中使用Windows API和COM(即非托管代码,其中包含错误或未正确管理),则可能会在您的应用程序中发生这种情况。我也发现在使用钢笔之类的某些物体后可能会导致问题。
我个人遭遇了内存异常,这可能是由于网络应用程序中的内存泄漏造成的,但并非排他性。 (OOM也可以来自固定Pinning Artical)。如果您没有收到OOM错误或需要确认是否是导致它的内存泄漏,那么唯一的方法是对您的应用程序进行概要分析。
我也会尝试确保以下内容:
a)实现Idisposable的所有东西都是使用finally块或者using语句来处理的,这些包括画笔,笔等等(有些人认为此外的所有东西都是零)
b)使用finally或using语句再次关闭任何具有close方法的东西(虽然我发现使用并不总是关闭,具体取决于你是否在using语句之外声明了对象)
c)如果您使用的是非托管代码/ Windows API,则会在之后正确处理。 (有些人有清理方法来释放资源)
希望这有帮助。
答案 4 :(得分:19)
如果您需要在.NET中诊断内存泄漏,请检查以下链接:
http://msdn.microsoft.com/en-us/magazine/cc163833.aspx
http://msdn.microsoft.com/en-us/magazine/cc164138.aspx
这些文章描述了如何创建流程的内存转储以及如何对其进行分析,以便您可以首先确定泄漏是否是非托管或托管的,如果是托管的,则如何确定它的来源。
Microsoft还有一个更新的工具来帮助生成故障转储,以替换称为DebugDiag的ADPlus。
答案 5 :(得分:16)
使用来自Microsoft http://www.microsoft.com/downloads/details.aspx?familyid=86ce6052-d7f4-4aeb-9b7a-94635beebdda&displaylang=en的CLR Profiler是一种很好的方法,可以确定哪些对象占用内存,哪些执行流程可以创建这些对象,还可以监视哪些对象位于堆上的哪个位置(碎片,LOH)等等。)。
答案 6 :(得分:15)
关于垃圾收集器如何工作的最佳解释是在Jeff Richters CLR via C#一书中,(第20章)。阅读本文为理解对象如何持续存在奠定了基础。
意外生根对象的最常见原因之一是将事件挂在课外。如果您挂钩外部事件
e.g。
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)的研讨会得到了这个提示。
答案 7 :(得分:14)
我想在托管环境中,泄密就是你不必要地引用大量的内存。
答案 8 :(得分:11)
为什么人们认为.NET中的内存泄漏与其他任何泄漏都不一样?
内存泄漏是指您附加到资源但不放过它。您可以在托管和非托管编码中执行此操作。
关于.NET和其他编程工具,有关于垃圾收集的想法,以及其他最小化使应用程序泄漏的情况的方法。 但防止内存泄漏的最佳方法是,您需要在正在使用的平台上了解底层内存模型以及工作原理。
相信GC和其他魔法会清理你的混乱是内存泄漏的简短方法,并且以后很难找到。
当编码不受管理时,你通常要确保清理,你知道你掌握的资源,你有责任清理,而不是看门人。
另一方面,在.NET中,很多人认为GC会清理所有内容。嗯,它为你做了一些,但你需要确保它是如此。 .NET确实包含很多东西,所以你并不总是知道你是在处理托管资源还是非托管资源,而是需要确定你正在处理什么。处理字体,GDI资源,活动目录,数据库等通常是您需要注意的事项。
在管理方面,我会戴上脖子 这条线说它确实消失了一次 该过程被杀死/移除。
我看到很多人都有这个,我真的希望这会结束。你不能要求用户终止你的应用程序来清理你的烂摊子! 看看浏览器,可以是IE,FF等,然后打开,比如谷歌阅读器,让它停留几天,看看会发生什么。
如果您随后在浏览器中打开另一个选项卡,浏览某个站点,然后关闭托管导致浏览器泄漏的其他页面的选项卡,您认为浏览器会释放内存吗? IE不是这样。在我的电脑上,如果我使用谷歌阅读器,IE将在很短的时间内(约3-4天)轻松吃掉1 GiB的内存。有些新闻报道甚至更糟。
答案 9 :(得分:10)
我想在一个托管环境中,一个 你要保持泄密 对大块的不必要的引用 记忆力。
绝对。此外,在适当的情况下,不在一次性对象上使用.Dispose()方法会导致内存泄漏。最简单的方法是使用一个使用块,因为它会在结尾自动执行.Dispose():
StreamReader sr;
using(sr = new StreamReader("somefile.txt"))
{
//do some stuff
}
如果你创建一个使用非托管对象的类,如果你没有正确实现IDisposable,你可能会导致你班级用户的内存泄漏。
答案 10 :(得分:9)
所有内存泄漏都通过程序终止来解决。
内存泄漏,操作系统可能会决定代表您解决问题。
答案 11 :(得分:8)
我将同意伯纳德关于.net内存泄漏的内容。
您可以对应用程序进行分析以查看其内存使用情况,并确定如果它不应该管理大量内存,您可以说它有泄漏。
在管理方面,我会把我的脖子放在线上,说它一旦杀死/移除过程就会消失。
非托管代码是它自己的野兽,如果其中存在泄漏,它将遵循标准内存。泄漏定义。
答案 12 :(得分:7)
还要记住,.NET有两个堆,一个是大对象堆。我相信大约85k或更大的物体被放在这堆上。此堆具有与常规堆不同的生存期规则。
如果要创建大型内存结构(字典或列表),那么谨慎查找具体规则是什么。
至于在进程终止时回收内存,除非你运行的是Win98或它的等价物,否则一切都会在终止时释放回操作系统。唯一的例外是跨进程打开的事情,而另一个进程仍然打开资源。
COM对象可能很棘手。如果您始终使用IDispose
模式,那么您将是安全的。但我遇到了一些实现IDispose
的互操作程序集。这里的关键是在完成后调用Marshal.ReleaseCOMObject
。 COM对象仍然使用标准的COM引用计数。
答案 13 :(得分:6)
我发现.Net Memory Profiler在.Net中发现内存泄漏时提供了很好的帮助。它不像Microsoft CLR Profiler那样免费,但在我看来更快,更重要。甲
答案 14 :(得分:1)
一个定义是:无法释放无法访问的内存,在执行分配过程期间无法再分配给新进程。它主要可以通过GC技术固化或通过自动化工具检测。
有关详细信息,请访问http://all-about-java-and-weblogic-server.blogspot.in/2014/01/what-is-memory-leak-in-java.html。