什么策略和工具对于在.NET中查找内存泄漏有用?

我写了C ++ 10年。 我遇到了记忆问题,但可以用合理的努力来修复它们。

在过去的几年里,我一直在写C#。 我发现我仍然有很多内存问题。 他们很难诊断和修复由于非决定因素,因为C#的哲学是,当你非常确定的时候,你不应该担心这样的事情。

我发现的一个特别的问题是我必须明确地处理和清理代码中的所有内容。 如果我不这样做的话,那么内存分析器并没有真正的帮助,因为有太多的糠floating浮动,你不能在他们试图向你展示的所有数据中find泄漏。 我想知道我是否有错误的想法,或者我得到的工具不是最好的。

什么样的策略和工具对于解决.NET中的内存泄漏有用?

当我怀疑内存泄漏时,我使用Scitech的MemProfiler 。

到目前为止,我发现它非常可靠和强大。 至less有一次它拯救了我的培根。

GC在.NET中运行得非常好,但是和其他语言或平台一样,如果编写错误的代码,就会发生不好的事情。

只要忘记处理问题,请尝试本博客文章中描述的解决scheme 。 这是本质:

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 

我们已经在我们的项目中使用了Red Gate软件的Ants Profiler Pro 。 它适用于所有基于.NET语言的应用程序。

我们发现.NET垃圾收集器在清理内存中的对象方面非常“安全”(应该是这样)。 它会保留对象,因为我们可能会在将来某个时候使用它。 这意味着我们需要更加小心在内存中膨胀的对象的数量。 最后,我们将所有数据对象转换为“按需膨胀”(就在请求字段之前),以减less内存开销并提高性能。

编辑:这是我的意思是“按需膨胀”的进一步解释。 在我们的数据库对象模型中,我们使用父对象的属性来公开子对象。 例如,如果我们有一些logging在一对一的基础上引用了一些其他的“细节”或“查找”logging,我们可以这样构造它:

 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 

我们发现上面的系统在内存中有很多logging时会造成一些真实的内存和性能问题。 所以我们切换到一个系统,在这个系统中只有被请求的时候才会对对象进行充气,而数据库调用只在需要的时候完成:

 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 

事实certificate,这样做效率要高得多,因为在需要的时候,对象一直处于内存不足的状态(Get方法被访问)。 它在限制数据库命中方面提供了非常大的性能提升,并在内存空间上获得了巨大的收益。

除非应用程序不重要,否则在编写托pipe代码时仍然需要担心内存。 我会提出两点build议:首先, 通过C#阅读CLR,因为它可以帮助你理解.NET中的内存pipe理。 其次,学会使用像CLRProfiler (微软)这样的工具。 这可以让你知道是什么导致你的内存泄漏(例如,你可以看看你的大对象堆碎片)

你使用非托pipe代码? 如果你不使用非托pipe代码,根据微软的说法,传统意义上的内存泄漏是不可能的。

应用程序使用的内存可能不会被释放,所以应用程序的内存分配可能会在应用程序的整个生命周期内增长。

从如何识别Microsoft.com上的公共语言运行库中的内存泄漏

当您使用非托pipe代码作为应用程序的一部分时,.NET Framework应用程序中可能会发生内存泄漏。 此非托pipe代码可能会泄漏内存,而.NET Framework运行时无法解决该问题。

另外,一个项目可能只会出现内存泄漏。 如果许多大对象(如DataTable对象)被声明,然后添加到集合(如DataSet),则会发生此情况。 这些对象拥有的资源可能永远不会被释放,并且资源在整个程序运行中仍然存在。 这似乎是一个泄漏,但实际上这只是程序中分配内存方式的一个症状。

为了处理这种types的问题,你可以实现IDisposable 。 如果你想看看处理内存pipe理的一些策略,我会build议寻找IDisposable,XNA,内存pipe理,因为游戏开发者需要有更多可预测的垃圾回收,所以必须强制GC去做。

一个常见的错误是不删除订阅对象的事件处理程序。 事件处理程序订阅将防止对象被回收。 另外,请看使用语句,它允许您为资源的生命周期创build有限的范围。

这个博客有一些非常精彩的演练使用windbg和其他工具来追踪所有types的内存泄漏。 优秀的阅读能力来培养你的技能

我只是在Windows服务中发生内存泄漏,我修复了。

首先,我尝试了MemProfiler 。 我发现它真的很难使用,并不是所有用户友好的。

然后,我使用了更容易使用的JustTrace ,并提供了有关未正确放置的对象的更多详细信息。

这让我很容易解决内存泄漏。

如果您正在观察的泄漏是由于caching实现失控导致的,则可能需要考虑使用WeakReference。 这可以帮助确保内存在必要时被释放。

但是,恕我直言,最好考虑一个定制的解决scheme – 只有你真的知道你需要多长时间保持对象,所以为你的情况devise适当的内务代码通常是最好的方法。

要记住的最好的事情是跟踪对象的引用。 最后挂着你不再关心的对象的引用是很容易的。 如果你不再使用某些东西,就把它除掉。

习惯于使用滑动过期的caching提供程序,这样如果在所需的时间窗口中没有引用某些内容,则会对其进行解除引用和清理。 但如果它被访问了很多,它会在内存中说。

最好的工具之一是使用Windowsdebugging工具 ,并使用adplus进行内存转储,然后使用windbgsos插件分析进程内存,线程和调用堆栈。

您也可以使用此方法来识别服务器上的问题,在安装工具之后,共享目录,然后使用(net use)连接到服务器的共享,并进行崩溃或挂起进程转储。

然后离线分析。

大枪 – Windowsdebugging工具

这是一个惊人的工具集合。 您可以使用它分析托pipe堆和非托pipe堆,并且可以离线执行。 这对debugging我们的ASP.NET应用程序非常方便,因为内存过度使用,这些应用程序可以继续回收。 我只需创build一个在生产服务器上运行的活动进程的完整内存转储,所有分析在WinDbg中都是离线完成的。 (事实certificate,一些开发者正在过度使用内存中的Session存储。)

“如果被破坏,这是…”博客有关于这个问题非常有用的文章。

在我的一个托pipe应用程序的修复程序中,我有同样的事情,比如如何validation我的应用程序在下次更改后不会有相同的内存泄漏,所以我写了类似于Object Release Verification框架的东西,请看看NuGet包ObjectReleaseVerification 。 你可以在这里find一个示例https://github.com/outcoldman/OutcoldSolutions-ObjectReleaseVerification-Sample ,以及关于这个示例的信息http://outcoldman.ru/en/blog/show/322