155

我写了 10 年的 C++。我遇到了内存问题,但可以通过合理的努力来解决它们。

在过去的几年里,我一直在编写 C#。我发现我仍然有很多记忆问题。由于不确定性,它们很难诊断和修复,而且因为 C# 的理念是,当你非常肯定地这样做时,你不应该担心这些事情。

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

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

4

15 回答 15

52

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

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

GC 在 .NET IMO 中运行良好,但就像任何其他语言或平台一样,如果你编写了糟糕的代码,就会发生糟糕的事情。

于 2008-09-25T15:43:44.177 回答
41

仅针对忘记处置问题,请尝试此博客文章中描述的解决方案。这是本质:

    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
于 2008-09-25T16:26:38.287 回答
17

我们在项目中使用了 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 方法)。它在限制数据库命中和内存空间方面提供了非常大的性能提升。

于 2008-09-25T15:51:09.167 回答
7

在编写托管代码时,您仍然需要担心内存,除非您的应用程序是微不足道的。我将建议两件事:首先,通过 C# 阅读 CLR,因为它将帮助您了解 .NET 中的内存管理。其次,学会使用像CLRProfiler (Microsoft) 这样的工具。这可以让您了解导致内存泄漏的原因(例如,您可以查看大对象堆碎片)

于 2008-09-25T15:50:53.053 回答
6

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

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

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

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

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

为了处理此类问题,您可以实现IDisposable。如果您想了解一些处理内存管理的策略,我建议您搜索IDisposable、XNA、内存管理,因为游戏开发人员需要更多可预测的垃圾收集,因此必须强制 GC 做它的事情。

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

于 2008-09-25T16:41:01.857 回答
5

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

于 2008-10-01T00:47:37.203 回答
5

我刚刚修复了 Windows 服务中的内存泄漏。

首先,我尝试了 MemProfiler。我发现它真的很难使用,而且一点也不友好。

然后,我使用了 JustTrace,它更易于使用,并为您提供有关未正确处理的对象的更多详细信息。

它让我很容易解决内存泄漏问题。

于 2015-10-22T15:03:29.867 回答
3

如果您观察到的泄漏是由于缓存实现失控造成的,那么您可能需要考虑使用 Wea​​kReference。这有助于确保在必要时释放内存。

但是,恕我直言,最好考虑定制解决方案 - 只有您真正知道需要将对象保留多长时间,因此为您的情况设计适当的内务管理代码通常是最好的方法。

于 2008-09-25T16:22:09.920 回答
3

Big guns - Windows 调试工具

这是一个惊人的工具集合。您可以使用它分析托管和非托管堆,并且可以离线进行。这对于调试由于内存过度使用而不断回收的 ASP.NET 应用程序之一非常方便。我只需要创建一个在生产服务器上运行的活动进程的完整内存转储,所有分析都是在 WinDbg 中离线完成的。(事实证明,一些开发人员过度使用了内存中的会话存储。)

“如果坏了就是……”博客有关于这个主题的非常有用的文章。

于 2008-09-25T16:44:27.323 回答
3

我更喜欢Jetbrains 的dotmemory

于 2018-03-02T19:12:13.640 回答
2

要记住的最好的事情是跟踪对对象的引用。很容易挂起对您不再关心的对象的引用。如果您不再使用某些东西,请摆脱它。

习惯于使用具有滑动到期的缓存提供程序,因此如果在所需的时间窗口内没有引用某些内容,则会取消引用并清理它。但如果它被大量访问,它会在内存中显示。

于 2008-09-25T15:55:53.960 回答
2

最好的工具之一是使用Debugging Tools for Windows ,并使用adplus获取进程的内存转储,然后使用windbgsos插件来分析进程内存、线程和调用堆栈。

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

然后离线分析。

于 2008-09-25T16:12:48.397 回答
2

在对托管应用程序进行了一次修复之后,我遇到了同样的事情,比如如何验证我的应用程序在下一次更改后不会有相同的内存泄漏,所以我写了类似对象发布验证框架的东西,请看一下NuGet 包ObjectReleaseVerification。您可以在此处找到示例https://github.com/outcoldman/OutcoldSolutions-ObjectReleaseVerification-Sample以及有关此示例的信息http://outcoldman.ru/en/blog/show/322

于 2012-09-17T04:01:37.843 回答
0

从 Visual Studio 2015 开始考虑使用开箱即用的内存使用诊断工具 来收集和分析内存使用数据。

内存使用工具可让您拍摄托管和本机内存堆的一个或多个快照,以帮助了解对象类型的内存使用影响。

于 2018-05-04T23:03:07.907 回答
0

我使用它的 DotMemory 的最佳工具之一。您可以将此工具用作 VS 中的扩展。运行您的应用程序后,您可以分析您的应用程序使用的内存的每个部分(按对象、命名空间等)并拍摄一些快照, 与其他快照比较。 点内存

于 2020-04-07T07:48:31.773 回答