哪些策略和工具可用于查找 .NET 中的内存泄漏?
Posted
技术标签:
【中文标题】哪些策略和工具可用于查找 .NET 中的内存泄漏?【英文标题】:What strategies and tools are useful for finding memory leaks in .NET? 【发布时间】:2010-09-13 03:24:06 【问题描述】:我写了 10 年的 C++。我遇到了内存问题,但只要付出合理的努力就可以解决。
在过去的几年里,我一直在编写 C#。我发现我仍然有很多记忆问题。由于不确定性,它们很难诊断和修复,而且因为 C# 理念是,当你非常确定这样做时,你不应该担心这些事情。
我发现的一个特殊问题是我必须明确地处理和清理代码中的所有内容。如果我不这样做,那么内存分析器就没有真正的帮助,因为有太多的谷壳漂浮在你身上,你无法在他们试图向你展示的所有数据中找到泄漏。我想知道我是否有错误的想法,或者我拥有的工具不是最好的。
什么样的策略和工具对解决 .NET 中的内存泄漏有用?
【问题讨论】:
您帖子的标题与您帖子中的问题不完全匹配。我建议你更新你的标题。 你是对的。抱歉,我有点厌倦了我正在寻找的当前泄漏!标题已更新。 @Scott:不要厌倦 .NET,这不是问题。你的代码是。 是的,我的代码,或者我有幸使用的第三方库。 @Scott:看我的回答。 MemProfiler 是值得的。使用它还将使您对 .NET GC 世界的理解达到一个全新的水平。 【参考方案1】:当我怀疑内存泄漏时,我使用 Scitech 的 MemProfiler。
到目前为止,我发现它非常可靠且功能强大。它至少有一次救了我的培根。
GC 在 .NET IMO 中运行良好,但就像任何其他语言或平台一样,如果您编写了糟糕的代码,就会发生糟糕的事情。
【讨论】:
是的,我尝试了这个,它帮助我找到了一些棘手的漏洞。我发现最大的泄漏是由第三方库在他们通过互操作访问的非托管代码中引起的。这个工具检测到非托管代码和托管代码中的泄漏给我留下了深刻的印象。 我接受了这个作为答案,因为它最终对我有用,但我认为所有其他答案都非常有用。顺便说一下,这个工具通常被称为 SciTech 的 Mem Profiler!【参考方案2】:在编写托管代码时,您仍然需要担心内存问题,除非您的应用程序是微不足道的。我将建议两件事:首先,通过 C# 阅读 CLR,因为它将帮助您了解 .NET 中的内存管理。其次,学习使用CLRProfiler(Microsoft)之类的工具。这可以让您了解导致内存泄漏的原因(例如,您可以查看大型对象堆碎片)
【讨论】:
是的。 CLRPRofiler 非常酷。当试图挖掘它为您提供的已分配对象的视图时,它可能会因为信息而变得有点爆炸性,但一切都在那里。这绝对是一个很好的起点,尤其是免费的。【参考方案3】:我们在项目中使用了 Red Gate 软件的Ants Profiler Pro。它适用于所有基于 .NET 语言的应用程序。
我们发现 .NET 垃圾收集器在清理内存对象时非常“安全”(应该如此)。它将保留对象只是因为我们可能在未来的某个时候会使用它。这意味着我们需要更加小心我们在内存中膨胀的对象数量。最后,我们将所有数据对象转换为“按需膨胀”(就在请求字段之前),以减少内存开销并提高性能。
编辑:这是我所说的“按需膨胀”的进一步解释。在我们数据库的对象模型中,我们使用父对象的属性来公开子对象。例如,如果我们有一些记录在一对一的基础上引用了一些其他“详细信息”或“查找”记录,我们将这样构建它:
class ParentObject
Private mRelatedObject as New CRelatedObject
public Readonly property RelatedObject() as CRelatedObject
get
mRelatedObject.getWithID(RelatedObjectID)
return mRelatedObject
end get
end property
End class
我们发现,当内存中有大量记录时,上述系统会产生一些实际内存和性能问题。所以我们切换到一个系统,其中对象仅在请求时才被膨胀,并且仅在必要时进行数据库调用:
class ParentObject
Private mRelatedObject as CRelatedObject
Public ReadOnly Property RelatedObject() as CRelatedObject
Get
If mRelatedObject is Nothing
mRelatedObject = New CRelatedObject
End If
If mRelatedObject.isEmptyObject
mRelatedObject.getWithID(RelatedObjectID)
End If
return mRelatedObject
end get
end Property
end class
事实证明,这样做效率更高,因为对象在需要它们之前一直保持在内存之外(访问 Get 方法)。它在限制数据库命中和内存空间方面提供了非常大的性能提升。
【讨论】:
我支持这个产品。这是我用过的最好的分析器之一。 我发现分析器非常适合查看性能问题。但是,内存分析工具很差。我用这个工具发现了一个漏洞,但它无法帮助我确定泄漏的原因。如果泄漏恰好发生在非托管代码中,它对您毫无帮助。 好的,新版本 5.1 好多了。更好地帮助您找到泄漏的原因(尽管 - ANTS 告诉我他们将在下一个版本中修复它仍然存在一些问题)。虽然仍然不执行非托管代码,但如果您不介意非托管代码,现在这是一个非常好的工具。【参考方案4】:要记住的最好的事情是跟踪对对象的引用。很容易挂起对您不再关心的对象的引用。 如果您不再使用某些东西,请摆脱它。
习惯于使用具有滑动过期时间的缓存提供程序,这样如果在所需的时间窗口内没有引用某些内容,它就会被取消引用并清理。但如果它被大量访问,它会在内存中显示。
【讨论】:
【参考方案5】:最好的工具之一是使用 Debugging Tools for Windows,并使用 adplus 对进程进行内存转储,然后使用 windbg 和 sos 用于分析进程内存、线程和调用堆栈的插件。
您也可以使用此方法来识别服务器上的问题,安装工具后,共享目录,然后使用 (net use) 从服务器连接到共享,然后执行崩溃或挂起进程转储。
然后离线分析。
【讨论】:
是的,这很有效,特别是对于更高级的东西或诊断发布的软件中的问题,而您不能轻易地将调试器附加到这些问题上。这个博客有很多关于如何使用这些工具的技巧:blogs.msdn.com/tess【参考方案6】:如果您观察到的泄漏是由于缓存实现失控造成的,那么您可能需要考虑使用 WeakReference。这有助于确保在必要时释放内存。
但是,恕我直言,最好考虑定制解决方案 - 只有您真正知道需要将对象保留多长时间,因此根据您的情况设计适当的内务管理代码通常是最好的方法。
【讨论】:
【参考方案7】:对于忘记处置问题,请尝试the solution described in this blog post。本质是这样的:
public void Dispose ()
// Dispose logic here ...
// It's a bad error if someone forgets to call Dispose,
// so in Debug builds, we put a finalizer in to detect
// the error. If Dispose is called, we suppress the
// finalizer.
#if DEBUG
GC.SuppressFinalize(this);
#endif
#if DEBUG
~TimedLock()
// If this finalizer runs, someone somewhere failed to
// call Dispose, which means we've failed to leave
// a monitor!
System.Diagnostics.Debug.Fail("Undisposed lock");
#endif
【讨论】:
我宁愿抛出异常而不是 Debug.Fail【参考方案8】:您是否使用非托管代码?如果你不使用非托管代码,根据微软的说法,传统意义上的内存泄漏是不可能的。
应用程序使用的内存可能不会被释放,因此应用程序的内存分配可能会在应用程序的整个生命周期内增长。
来自How to identify memory leaks in the common language runtime at Microsoft.com
.NET 中可能发生内存泄漏 使用时的框架应用程序 非托管代码作为 应用。这个非托管代码可以 内存泄漏和 .NET Framework 运行时无法解决该问题。
此外,一个项目只能 似乎有内存泄漏。这 如果许多大的情况可能会发生 对象(例如 DataTable 对象) 被声明然后添加到 集合(例如 DataSet)。这 这些对象拥有的资源可能 永远不会被释放,资源 在整个运行过程中都活着 该程序。这似乎是一个 泄漏,但实际上它只是一个 记忆方式的症状 在程序中分配。
为了处理此类问题,您可以实现IDisposable。如果您想了解一些处理内存管理的策略,我建议您搜索IDisposable、XNA、内存管理,因为游戏开发人员需要更多可预测的垃圾收集,因此必须强制 GC做它的事。
一个常见的错误是不删除订阅对象的事件处理程序。事件处理程序订阅将防止对象被回收。此外,请查看 using 语句,它允许您为资源的生命周期创建有限范围。
【讨论】:
见blogs.msdn.com/tess/archive/2006/01/23/…。内存泄漏是否“传统”并不重要,它仍然是泄漏。 我明白你的意思——但程序对内存的低效分配和重用与内存泄漏不同。 很好的答案,感谢您提醒我事件处理程序可能很危险。 @Timothy Lee Russel:如果无限量 (1) 的内存在变得无用 (2) 后仍然可以同时分配(根),而系统中没有任何东西具有解除根所需的信息和动力及时,这是内存泄漏。即使内存可能有一天会被释放,如果在此之前可能会积累足够多的无用的东西来阻塞系统,这就是泄漏。 (1)大于O(N),N为有用分配量; (2) 如果删除对它的引用不会影响程序功能,那么这些东西是无用的。 @Timothy Lee Russel:正常的“内存泄漏”模式发生在一个实体代表另一个实体持有内存时,期望在不再需要时被告知,但后者在不告诉第一个实体的情况下放弃了实体。持有内存的实体并不真正需要它,但无法确定。【参考方案9】:大炮 - Debugging Tools for Windows
这是一个惊人的工具集合。您可以使用它分析托管和非托管堆,并且可以离线进行。这对于调试我们的一个 ASP.NET 应用程序非常方便,该应用程序由于内存过度使用而不断回收。我只需要创建一个在生产服务器上运行的活动进程的完整内存转储,所有分析都是在 WinDbg 中离线完成的。 (事实证明,一些开发人员过度使用了内存中的 Session 存储。)
"If broken it is..." 博客有关于这个主题的非常有用的文章。
【讨论】:
【参考方案10】:This blog 有一些非常精彩的演练,使用 windbg 和其他工具来追踪所有类型的内存泄漏。优秀的阅读来发展你的技能。
【讨论】:
【参考方案11】:在我对托管应用程序进行了一次修复之后,我遇到了同样的事情,比如如何验证我的应用程序在下一次更改后不会有同样的内存泄漏,所以我写了类似对象发布验证框架的东西,请采纳查看 NuGet 包ObjectReleaseVerification。你可以在这里找到一个样本https://github.com/outcoldman/OutcoldSolutions-ObjectReleaseVerification-Sample,以及关于这个样本的信息http://outcoldman.ru/en/blog/show/322
【讨论】:
【参考方案12】:我刚刚修复了 Windows 服务中的内存泄漏。
首先,我尝试了MemProfiler。我发现它真的很难使用,而且一点也不友好。
然后,我使用了JustTrace,它更易于使用,并为您提供有关未正确处理的对象的更多详细信息。
它让我很容易解决内存泄漏问题。
【讨论】:
【参考方案13】:我更喜欢 Jetbrains 的 dotmemory
【讨论】:
你可能是唯一的一个:) 我也试过了。我认为这是一个很好的工具。易于使用,信息丰富。集成到 Visual Studio 在我们的例子中,当解决内存泄漏问题时,Visual Studio 快照工具崩溃/没有快照。 Dotmemory 保持冷静,并(看似)轻松地处理了 3 GB 以上的多个快照。【参考方案14】:从 Visual Studio 2015 开始考虑使用开箱即用的Memory Usage diagnostic tool 来收集和分析内存使用数据。
内存使用工具可让您拍摄托管和本机内存堆的一个或多个快照,以帮助了解对象类型对内存使用的影响。
【讨论】:
【参考方案15】:我使用它的 DotMemory 的最佳工具之一。您可以将此工具用作 VS 中的扩展。运行您的应用程序后,您可以分析您的应用程序使用的内存的每个部分(按对象、命名空间等)并采取一些快照,将其与其他快照进行比较。 DotMemory
【讨论】:
以上是关于哪些策略和工具可用于查找 .NET 中的内存泄漏?的主要内容,如果未能解决你的问题,请参考以下文章