【问题标题】:Unmanaged memory leak in mscorwks help analyzemscorwks 中的非托管内存泄漏帮助分析
【发布时间】:2012-09-25 10:55:22
【问题描述】:

故事:

我们在 .NET 2.0 应用程序中面临非托管内存泄漏。启动后的进程消耗大约 150MB(其中大部分是 .NET 托管、对象状态等)。 运行大约 12 小时后,进程消耗了 800MB,接下来的 12 小时后,进程有大约 1.8GB 的​​ RAM。 我刚刚尝试了 JetBrains .NET Memory Profiler、ANTS、.NET Memory Profiler(以及市场上可能有的 2 个下一个 mem 配置文件),这些都没有帮助我,因为我后来检测到我们的进程在未管理的非托管区域中消耗了那么多内存。为了检测到这一点,我使用了带计数器的 Perf 监视器:Private Bytes(Process) 和 # Bytes in All Heaps (.NET CLR Memory),其中 Private Bytes 消耗了进程分配的所有内存的大约 90%。这就是我切换到非托管调试的原因。

调试诊断: 所以我在进程上运行 debugdiag 并获得完整转储,这是它的快照:

  • mscorwks.dll(一个已知的 Windows 内存管理器)负责 781,73 MB 的未完成分配。这些分配 似乎源自以下模块和 功能:

  • ntdll.dll(一个已知的 Windows 内存管理器)负责 98,24 MB 的未完成分配。这些分配似乎源自以下模块和功能:

按分配计数排名前 4 位的函数

  • mscorwks!EEHeapAlloc+15b -- 80 957 个分配
  • mscorwks!CLRMapViewOfFileEx+4a -- 4 171 个分配

按分配大小排列的前 4 个函数

  • mscorwks!EEVirtualAlloc+15b -- 117,50 MB
  • mscorwks!EEHeapAlloc+15b -- 15,03 MB

发现有趣的日志:

功能详情

Function mscorwks!EEVirtualAlloc+15b

  • 分配类型虚拟内存分配
  • 分配计数 1471 个分配
  • 分配大小 117,50 MBytes
  • 泄漏概率 73%

Function mscorwks!EEHeapAlloc+15b

  • 分配类型堆分配
  • 分配计数 80957 个分配
  • 分配大小 15,03 MBytes
  • 泄漏概率 72%

Function mscorwks!CExecutionEngine::CheckThreadState+fe

  • 分配类型堆分配
  • 堆句柄 0x00000000`00000000
  • 分配计数 2 个分配
  • 分配大小 304 字节
  • 泄漏概率 98%

Function mscorwks!CLRMapViewOfFileEx+4a

  • 分配类型虚拟内存分配
  • 分配计数 4171 个分配
  • 分配大小 0 字节
  • 泄漏概率 73%

我希望有人将我推向正确的方向,我如何从这个转储中找到内存泄漏?我能够将转储加载到 windbg 并运行标准的 windbg 命令集,但我不知道哪个是能够隔离泄漏的正确命令。

如果有人想提供帮助,我可以提供完整转储。

【问题讨论】:

  • 这里有一些可能有用的信息? blogs.msdn.com/b/tess/archive/2009/10/09/…您是否在代码中使用了任何您可能不会清理的 COM 对象?
  • Joe:已经和其他 20 多岁的人一起阅读这篇文章,但仍然不知道在哪里以及如何找到泄漏。我有来自 DebugDiag 的详细日志,但是当我不知道如何“阅读”它时它是无用的:-|
  • 我建议您避免关注非托管方面,除非您的应用程序进行主要的非托管内存操作。在 .NET 应用程序中,我看到的内存问题的主要原因是迄今为止意外的引用 - 未删除事件处理程序是主要原因。更多在下面的答案中。
  • 私有字节意味着内存不与其他进程共享。托管堆不在进程之间共享,因此如果托管堆很大,私有字节会相应增长。根据您的描述,我不清楚您是否在所有堆中看到大量的私有字节和少量的 # 字节。如果这两个数字都很大,那么您正在查看托管内存。
  • 我会试一试的。你能把转储上传到某个地方并分享链接吗?

标签: c# memory-leaks windbg


【解决方案1】:

使用弱引用可能会减少内存使用http://en.wikipedia.org/wiki/Weak_reference .. 这很一般,但一旦它为我节省了几乎几 GB 的时间

【讨论】:

  • 我现在不太明白这对我有什么帮助?我已经完成了项目(由 50 多个 .NET DLL 组成)并且在某处有内存泄漏,所以我需要以某种方式隔离它..
  • 我的意思是......泄漏很可能是由于GC没有收集一些obj引用stackoverflow.com/questions/707421/…
【解决方案2】:

我最喜欢的调试 .NET 和 Silverlight 内存泄漏的方法是使用 SOS 扩展。请参阅此处进行快速演练:http://blogs.msdn.com/b/ricom/archive/2004/12/10/279612.aspx

我通常做的是:

  1. !DumpHeap -stat 获取内存中存在哪些对象的列表;这通常表明问题的根源(例如,如果我看到一百万只应该使用一次并丢弃的小物件)。
  2. 一旦我知道是什么类型的对象导致它,我使用 !DumpHeap 转储这些对象的列表并随机获取其中一些对象的根 (!GCRoot)。这通常表明哪个对象意外持有对泄漏对象的引用。

这假设您正在处理托管内存泄漏(引用被保存在您不期望的位置)。如果您正在处理非托管内存泄漏,这将无济于事,但除非您的应用程序执行大量手动非托管内存管理(例如 P/Invoke 的对象编组),否则这种情况不太可能发生。

【讨论】:

  • 已经使用了 SOS 扩展,但我尝试的所有示例都不能帮助我隔离泄漏。
  • 正如我在帖子中所说,我几乎使用了所有 .NET 托管内存配置文件,它们显示我的进程已分配了大约 10% 的所有内存进程消耗(根据任务管理器 perf.monitor计数器等...)所以我认为这确实是一个非托管内存泄漏。
  • 运行 !dumpheap -stat 并发布结果。不要只是凭直觉。获取硬数据。如果 !dumpheap -stat 和 !eeheap -gc 表明您的托管堆很小,那么您可以放心地转向非托管堆。您在上面引用为“泄漏者”的分配器是 CLR 分配 GC 堆段的函数。如果这个问题不是托管内存问题,我会感到惊讶。
【解决方案3】:

查看转储文件,它似乎确实是托管泄漏,只是不在托管堆上。转储显示托管堆非常小,但加载器堆为 1 GB。该流程有超过 35000 个动态程序集。我查看了其中一些,它们似乎是序列化(XML 和二进制)。看看这个blog post。它描述了一个类似的问题。

【讨论】:

  • 就是这样。使用 Xml 序列化程序对对象进行反序列化总是会生成新的动态程序集(由 .NET FW 本身),并且在几个小时后会有大约 35k 的程序集。因此,仅处理该数量的程序集就会消耗超过 1GB 的 RAM。感谢大家帮助隔离这个“泄漏”,这不是标准泄漏。
猜你喜欢
  • 2012-01-30
  • 2019-05-16
  • 2020-07-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-28
相关资源
最近更新 更多