【问题标题】:.NET Application Memory usage grow over time, memory leak or not?.NET 应用程序内存使用量随着时间的推移而增长,内存泄漏与否?
【发布时间】:2010-10-19 01:15:28
【问题描述】:

我编写了一个 .NET 应用程序,其中内存使用量随着时间的推移而增长。作为测试, 我将其设置为在 1 小时内以 15 分钟的间隔下载 800 * 5K(平均)大小的文件并进行 XML 转换并使用 DTComcontroller 生成清单。在大约 5 个小时的时间里,内存使用量从 35M 增长到大约 70M。

不确定这是否正常。我已经通过这样做来强迫它了

GC.collection();
GC.WaitForPendingFinalizers();

在每个工作循环结束时确保及时释放内存。

我已经确保在使用后关闭文件句柄、清除流或其他资源。有些对象只是在函数内部使用本地引用,我假设它们将在本地函数结束时被丢弃并与 GC.collction() 一起消失。

此外,Memory Profiler (ANTS) 显示,在每个工作循环的末尾都没有我的应用程序命名空间的对象,也没有可用源代码的新对象。在每个工作循环中创建的对象的新实例基本上称为 RuntimeModule、RuntimeAssembly 和 DeviceContext。看起来我对此无能为力?

这基本上是我的第一个应用程序,它假设运行 24/7,我真的不太了解内存泄漏。很可能,我的应用程序中的内存使用仍然存在问题。我只是不知道在哪里看这个阶段。

另外,我不知道它是否相关,我正在使用 .net Trace/debug 来保存日志。猜猜值得尝试关闭日志以查看内存使用量的增长。

更新: 让它看起来像 sslstate 和 sslstream 以及类 Reference Explorer 中的其他类的循环引用。也不确定这是否是 ANTS 生成图中的圆引用,这是否意味着我的代码中某处实际上存在圆引用?

我通过重用同一个对象来创建 XMLtransform 和 Manifest 进行了一些更改,并在完成工作迭代结束时使用它们后手动将每个对象设置为 null。现在内存大大减少了,在工作时仍然会增加私有内存的使用量,但速度要慢得多。

我认为对于我的特殊情况——在一个工作圈内创建许多对象,并且每个数据都保存在一个工作线程中。如果我没有显式释放对象引用,让 .NET GC 这样做会导致应用程序使用大量内存,因为它大部分时间都在工作,并且 GC 可能永远不会有机会进行内务处理,除非应用程序已手动关闭,我必须手动进行对象释放。
@WeNeedAnswers:

            XmlDocument doc = new XmlDocument();
            XmlTextReader textReader = new XmlTextReader(dataFile);
            textReader.Read();
            doc.Load(textReader);

【问题讨论】:

  • 应用永远不需要调用 GC.Collect。您不一定会提供帮助,而且很可能会使问题变得更糟。
  • 如果我直接在Console.WriteLine("Hello world")这样的线程中使用字符串,并且线程继续运行,.NET最终会释放该字符串使用的内存吗?

标签: .net memory


【解决方案1】:

任务管理器向您显示属于应用程序的内存量,该应用程序当时恰好被分页到实际内存中(工作集)。这可能就是您所看到的。

如果您查看进程使用的“私有字节”,这应该可以更好地指示所使用的内存量。修剪流程工作集时,此值不会更改。 this Microsoft KB article有更多信息。

您可以使用 Win32 API 调用 SetProcessWorkingSetSize(GetCurrentProcess(), -1, -1) 手动减少应用的工作集。

这是 Windows 在系统内存不足时无论如何都会执行的操作,但您自己控制何时发生这种情况可以让您将 .NET 应用程序的工作集剥离到其最小大小以进行调查。

顺便说一句,be very careful about calling GC.Collect()

【讨论】:

  • 谢谢,你和安德鲁都有很好的答案。经过几天对.NET中内存使用情况的研究,我终于弄清楚导致内存问题的原因是第三方DTComController。该对象的 DeInitialise 方法将消耗大量进程时间并导致内存无法正确释放 A.K.A 内存泄漏。所以我最终没有 DeInitialise 那个对象。并且应用程序在最大负载下运行数天没有问题,运行时间更长时仍然使用更多内存,这需要数月才能重新启动应用程序。
【解决方案2】:

处理对象处理的惯用方式是IDisposable 接口。这就是允许您使用using 构造来确保释放对象的原因。 @Joe 是对的;在大多数情况下,您不应该直接调用垃圾收集器。

【讨论】:

  • 如果我错了,请纠正,但我认为 IDisposable 接口是用于编写您自己的类并确保它会正确释放资源?但是由于内存分析器告诉我在每个工作循环之后我的应用程序命名空间下没有任何类实例,我认为这与我的内存使用/泄漏问题没有直接关系?
【解决方案3】:

我编写了一个 .NET 应用程序,其中 内存使用量随着时间的推移而增长。作为 测试一下,我设置下载800* 5K(平均)大小的文件和做 XML 转换并使用 DTComcontroller 来 生成清单,在 1 小时内 间隔 15 分钟。在一个 周期约5小时,内存占用 从 35M 增长到大约 70M。

这是个问题吗?如果没有(即足够的内存),这可能是原因。 GC 仅在内存开始变得稀缺时才起作用。恕我直言,70mb 也非常低。

【讨论】:

  • 实际上我们不确定这是否是一个问题。我们设置了测试,因此应用程序的压力几乎与操作系统和执行该过程的其他应用程序一样多。在我们的应用程序在周末或夜间运行后,取决于我们对它的压力程度,它的内存使用量将增长到大约 250M,如任务管理器所示。然后应用程序将继续为每个要处理的文件提供诸如“无法加载 dll”(我们正在使用 DTCom 对象)和“无法执行程序”之类的错误消息,并且在那之后似乎无法正常工作。
【解决方案4】:

不,不是泄漏,它是一个功能。不要做讨厌的 GC.Collect。如果我的测试盒由于缺乏资源而倒下,我只会担心它会泄漏,而不是因为系统使用的内存比我预期的要多。垃圾收集器在它真的运行之前不会运行。我不认为你对它施加的压力太大,它会担心它。

如果您真的很担心,请以更快的时间间隔将大量数据推送到您的系统中,然后看看会发生什么。如果它可以在突发模式下处理极端情况,那么通常当您移动到缓慢的涓流时,它应该更优雅地处理它。请记住,调用 GC.Collect 的成本很高,因此框架会在算法确定有必要时调用它。

您是通过流枚举数据还是只是将其放入内存中。如果前者比我的经验,你会得到一致的内存占用。如果是后者,根据 xml 的大小,它可能会导致它崩溃。

【讨论】:

  • @WeNeedAnswers,我一直在添加我的代码来做 XML,我认为这至少是问题的一部分?
  • 对不起,我不明白最后的评论。你添加了xml代码?
  • 嗨,我用我正在使用的 XML 代码更新了我的问题。您可以看到,当我将所有 XML 数据读入内存时,它使用的内存比通过流更多?
  • 这取决于您的缓冲区设置,如果您将数据流式传输到您的应用程序,如果使用大型 xml 有效负载将占用更少的内存。 Payload进来之后怎么处理,Xpath,XSLT,需要把文档加载成一大块。或者您是在零散地处理文档。如果逐个元素,逐个属性,您需要 XmlDocument 对象吗?如果你能避免我会的 XmlDocument。按顺序处理数据。取决于应用程序,但如果它是数据馈送,您通常可以缓冲并逐渐馈送。
  • 我会将数据视为通过 DBReader 传入的数据,并使用 XmlTextReader 将数据放入我的应用程序中,以便使用它。这取决于 xml 有效负载和应用程序的大小。有时您无法避免一次将文档全部加载到内存中。唯一一次在内存中加载 xml 的情况是 XSL 转换。
猜你喜欢
  • 2019-01-25
  • 1970-01-01
  • 2011-03-07
  • 2019-06-21
  • 1970-01-01
  • 1970-01-01
  • 2023-02-01
  • 2023-03-07
  • 1970-01-01
相关资源
最近更新 更多