从 .NET 角度来看:

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

解决方案

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

基本上,在 。网 当引用的对象是 root 的并且因此无法被垃圾收集时,就会发生内存泄漏。当您保留超出预期范围的引用时,会意外发生这种情况。

当您开始出现 OutOfMemoryExceptions 或内存使用量超出您的预期时,您就会知道存在泄漏(性能监视器 有很好的内存计数器)。

理解 。网的内存模型是避免它的最好方法。具体来说,了解垃圾收集器的工作原理以及引用的工作原理 - 再次,我建议您参阅电子书的第 7 章。另外,请注意常见的陷阱,最常见的可能是事件。如果反对 A 注册到对象上的事件 , ,然后反对 A 会一直坚持到对象为止 消失是因为 持有对 A. 。解决方案是在完成后取消注册您的事件。

当然,良好的内存配置文件将让您查看对象图并探索对象的嵌套/引用,以了解引用来自何处以及负责哪个根对象(红门蚁简介, JetBrains 点内存, 内存分析器 确实是不错的选择,或者您也可以使用纯文本 数据库管理工具求救, ,但我强烈推荐商业/视觉产品,除非您是真正的大师)。

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

其他提示

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

“不再使用”有不止一种含义,它可能意味着“不再引用它”,即完全不可恢复,或者它可能意味着引用、可恢复、未使用,但程序无论如何都会保留引用。仅后者适用于 .Net 完美管理的对象. 。然而,并非所有类都是完美的,在某些时候,底层非托管实现可能会永久泄漏该进程的资源。

在所有情况下,应用程序消耗的内存都超出了严格需要的内存。根据泄漏的数量,副作用可能从无到因过度收集而导致速度减慢,再到一系列内存异常,最后出现致命错误,然后强制进程终止。

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

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

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

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

一种有趣的方式是打开 性能监视器 并添加痕迹 所有堆中的 # 字节# 第二代合集 ,在每种情况下仅查看您的流程。如果执行特定功能导致总字节数增加,并且在下一次 Gen 2 收集后内存仍保持分配状态,您可能会说该功能泄漏了内存。

如何预防

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

当进程退出时,泄漏会消失吗?COM 互操作又如何呢?

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

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

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

我还将尝试确保以下几点:

a) 实现 Idisposable 的所有内容都可以使用 finally 块或 using 语句进行处理,其中包括画笔、钢笔等(有些人认为另外将所有内容设置为空)

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

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

希望这可以帮助。

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

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

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

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

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

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

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

Jeff Richters 对垃圾收集器工作原理的最好解释是 通过 C# 进行 CLR 书,(第 1 章)20)。阅读本文为理解对象如何持久存在奠定了良好的基础。

意外获取对象的最常见原因之一是连接类之外的事件。如果你连接一个外部事件

例如

SomeExternalClass.Changed += new EventHandler(HandleIt);

并且在处理时忘记取消挂钩,那么 SomeExternalClass 就会引用您的类。

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

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

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

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

然后运行 !dumpheap -stat -type <TypeName> 再次。如果数字没有下降,或者下降幅度没有你预期的那么大,那么你就有了进一步调查的基础。(我从一个研讨会上得到了这个提示 英戈·拉姆尔).

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

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

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

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

相信 GC 和其他魔法会清理你的混乱是导致内存泄漏的捷径,而且以后很难发现。

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

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

用托管术语,我将脖子上的脖子说一旦杀死/删除该过程,它确实消失了。

我看到很多人都有这种情况,我真的希望这会结束。您不能要求用户终止您的应用程序来清理您的混乱!看一下浏览器,可以是 IE、FF 等,然后打开,比如 Google Reader,让它停留几天,看看会发生什么。

如果您随后在浏览器中打开另一个选项卡,浏览某个网站,然后关闭托管导致浏览器泄漏的其他页面的选项卡,您认为浏览器会释放内存吗?IE 则不然。在我的电脑上,如果我使用 Google Reader,IE 将在短时间内(大约 3-4 天)轻松吃掉 1 GiB 内存。有些报纸的情况甚至更糟。

我猜想在托管环境中,泄漏是您要对周围的大部分记忆保持不必要的参考。

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

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

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

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

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

我同意 Bernard 的观点,即 .net 中的内存泄漏是什么。

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

用托管术语来说,我会冒着风险说,一旦进程被终止/删除,它就会消失。

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

另请记住,.NET 有两个堆,其中一个是大对象堆。我相信大约有 85k 或更大的对象被放置在这个堆上。该堆具有与常规堆不同的生命周期规则。

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

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

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

我发现 .Net 内存分析器 在查找 .Net 中的内存泄漏时非常有帮助。它不像 Microsoft CLR Profiler 那样免费,但在我看来更快、更切题。A

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

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

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top