哪些策略和工具对于在.NET中查找内存泄漏有用?
-
02-07-2019 - |
题
我写了10年的C ++。我遇到了内存问题,但可以通过合理的努力来修复它们。
在过去的几年里,我一直在写C#。我发现我仍然遇到很多内存问题。由于不确定性,它们很难诊断和修复,而且因为C#的理念是你绝对不必担心这些事情。
我发现的一个特殊问题是我必须在代码中明确地处理和清理所有内容。如果我不这样做,那么内存分析器并没有真正帮助,因为有太多的糠,你无法在他们试图向你展示的所有数据中发现泄漏。我想知道我是否有错误的想法,或者我的工具是不是最好的。
什么样的策略和工具对于解决.NET中的内存泄漏很有用?
解决方案
当我怀疑内存泄漏时,我使用了Scitech的 MemProfiler 。
到目前为止,我发现它非常可靠和强大。它至少有一次拯救了我的培根。
GC在.NET IMO中工作得非常好,但就像任何其他语言或平台一样,如果编写错误的代码,就会发生不好的事情。
其他提示
只是为了忘记处理问题,请尝试此博客文章中描述的解决方案。这就是本质:
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垃圾收集器非常“安全”。清理内存中的对象(应该是这样)。它会保留对象,因为我们可能将来某个时候使用它。这意味着我们需要更加小心我们在内存中膨胀的对象数量。最后,我们将所有数据对象转换为“按需扩充”。 (就在请求字段之前),以减少内存开销并提高性能。
编辑:这是对我所说的“按需膨胀”的进一步解释。在我们的数据库对象模型中,我们使用父对象的Properties来公开子对象。例如,如果我们有一些记录引用了其他一些“细节”,那么或“查找”或“查找”我们将以一对一的方式记录下来:
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方法)之前,对象被保留在内存之外。它在限制数据库命中率和内存空间大量增加方面提供了非常大的性能提升。
除非您的应用程序非常简单,否则在编写托管代码时仍需要担心内存问题。我将建议两件事:首先,通过C#读取 CLR,因为它将帮助您理解.NET中的内存管理。其次,学习使用像 CLRProfiler (Microsoft)这样的工具。这可以让您了解导致内存泄漏的原因(例如,您可以查看大对象堆碎片)
您使用的是非托管代码吗?如果你没有使用非托管代码,根据微软的说法,传统意义上的内存泄漏是不可能的。
然而,应用程序使用的内存可能无法释放,因此应用程序的内存分配可能会在应用程序的整个生命周期内增长。
来自如何识别Microsoft.com上公共语言运行库中的内存泄漏
.NET中可能发生内存泄漏 使用时的框架应用程序 非托管代码作为的一部分 应用。这个非托管代码可以 泄漏内存和.NET Framework 运行时无法解决这个问题。
此外,项目可能只是 似乎有内存泄漏。这个 如果很多大的情况可能会发生 对象(例如DataTable对象) 声明然后添加到a 集合(例如DataSet)。该 这些对象拥有的资源可能 永远不会被释放,资源 在整个运行期间都是活着的 该程序。这似乎是一个 泄漏,但实际上它只是一个 记忆方式的症状 正在计划中分配。
为了解决此类问题,您可以实施 IDisposable的。如果你想看一些处理内存管理的策略,我会建议搜索 IDisposable,XNA,内存管理,因为游戏开发者需要有更多可预测的垃圾收集,因此必须强制GC做它的事。
一个常见错误是不删除订阅对象的事件处理程序。事件处理程序订阅将阻止对象被回收。另外,请查看使用声明,允许您为资源的生命周期创建有限的范围。
此博客有一些非常精彩的演练使用windbg和其他工具来追踪所有类型的内存泄漏。优秀的阅读,以发展你的技能。
我刚刚修复了Windows服务中的内存泄漏问题。
首先,我尝试了 MemProfiler 。我发现它真的很难使用而且根本不是用户友好的。
然后,我使用了 JustTrace ,它更易于使用并且给出了有关未正确放置的对象的更多详细信息。
它让我很容易解决内存泄漏问题。
如果您观察到的泄漏是由于缓存实施失控造成的,那么可能想要考虑使用WeakReference的情况。这有助于确保在必要时释放内存。
然而,恕我直言,最好考虑一个定制的解决方案 - 只有你真的知道需要多长时间来保持物体周围,所以为你的情况设计合适的内务代码通常是最好的方法。
大枪 - 调试适用于Windows的工具
这是一个惊人的工具集合。您可以使用它分析托管和非托管堆,您可以脱机完成。这对于调试我们的ASP.NET应用程序非常方便,这些应用程序由于内存过度使用而保持回收。我只需要在生产服务器上创建一个完整的生存进程内存转储,所有分析都是在WinDbg中离线完成的。 (事实证明,一些开发人员过度使用了内存中的会话存储。)
“如果它被破坏了......”博客非常有用关于这个问题的文章。
要记住的最好的事情是跟踪对象的引用。最终会挂起对您不再关心的对象的引用。 如果你不再使用某些东西,那就去除吧。
习惯使用带有滑动过期的缓存提供程序,这样如果没有为所需的时间窗口引用某些内容,则会取消引用并清除它。但如果它被大量访问,它会在内存中说出来。
最好的工具之一是使用 Windows调试工具,并使用 adplus 对流程进行内存转储,然后使用 windbg 和 sos 插件分析进程内存,线程和调用堆栈。
您也可以使用此方法识别服务器上的问题,安装工具后,共享目录,然后使用(net use)从服务器连接到共享,并对进程进行崩溃或挂起转储。
然后离线分析。
在我的托管应用程序的一个修复程序之后,我有同样的事情,比如如何验证我的应用程序在下一次更改后不会有相同的内存泄漏,所以我写了类似对象发布验证框架,请带查看NuGet包 ObjectReleaseVerification 。你可以在这里找到一个样本 https://github.com/outcoldman/OutcoldSolutions-ObjectReleaseVerification-Sample,以及有关此示例的信息 http://outcoldman.ru/en/blog/show/322
我更喜欢来自Jetbrains的 dotmemory
从Visual Studio 2015开始考虑使用现成的 内存使用诊断工具 ,用于收集和分析内存使用情况数据。
“内存使用情况”工具允许您获取托管和本机内存堆的一个或多个快照,以帮助了解对象类型对内存使用的影响。