我写了C++ 10年.我遇到了内存问题,但可以通过合理的努力来修复它们.
在过去的几年里,我一直在写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.com上的公共语言运行库中的内存泄漏
当您将非托管代码用作应用程序的一部分时,.NET Framework应用程序中可能会发生内存泄漏.此非托管代码可能会泄漏内存,.NET Framework运行时无法解决该问题.
此外,项目可能只会出现内存泄漏.如果声明了许多大对象(例如DataTable对象),然后将其添加到集合(例如DataSet),则会发生这种情况.这些对象拥有的资源可能永远不会被释放,并且资源在整个程序运行时都处于活动状态.这似乎是一个泄漏,但实际上它只是在程序中分配内存的方式的一个症状.
要处理此类问题,您可以实现IDisposable.如果你想看一些处理内存管理的策略,我会建议搜索IDisposable,XNA,内存管理,因为游戏开发者需要有更多可预测的垃圾收集,所以必须强制GC做它的事情.
一个常见错误是不删除订阅对象的事件处理程序.事件处理程序订阅将阻止对象被回收.另外,请查看using语句,该语句允许您为资源的生命周期创建有限的范围.
这个博客有一些非常精彩的演练,使用windbg和其他工具来追踪所有类型的内存泄漏.优秀的阅读,以发展你的技能.
我刚刚修复了Windows服务中的内存泄漏问题.
首先,我尝试了MemProfiler.我发现它真的很难使用,而且根本不是用户友好的.
然后,我使用了更容易使用的JustTrace,并为您提供有关未正确放置的对象的更多详细信息.
它让我很容易解决内存泄漏问题.