【问题标题】:How do I find where memory is being used up in C#?如何在 C# 中找到内存被用完的地方?
【发布时间】:2011-07-20 01:38:03
【问题描述】:

我在 WP7 项目上运行了一个 C# XNA,我发现它在屏幕更改之间占用了内存并且没有返回它,最终导致内存不足异常。

我看了又看,但我一辈子都找不到这段记忆的去向。

有什么方法可以找出内存在哪里被使用以及为什么它没有被归还给设备?

感谢您的帮助!

【问题讨论】:

    标签: c# memory windows-phone-7 xna xna-4.0


    【解决方案1】:

    你可以试试Redgate's ANTS Memory Profiler(但要花钱),我不确定是否将它与 WP7 一起使用,但它适用于 C#。

    有一个免费试用版,因此您可以使用它来帮助定位问题。

    【讨论】:

      【解决方案2】:

      Eqatec 有一个适用于 WP7 的分析器。它不是内存分析器,但我会尝试看看它显示了什么。它可能有助于为您指明正确的方向。

      【讨论】:

        【解决方案3】:

        coding4fun toolkit 包含一个内存计数器,可帮助跟踪应用程序的内存使用情况。 Here's 文档和 article 演示其用法。

        【讨论】:

          【解决方案4】:

          使用CLR Profiler for the .NET Framework 2.0。默认情况下,XNA 4.0 不支持它,但 Crappy Coding 上的 Dave 有一个 workaround

          【讨论】:

          • 适用于 .NET 4.0 的 CLR Profiler 现已推出。看我的回答。
          【解决方案5】:

          在项目的 Windows 版本上使用 Microsoft 的 CLR Profiler for .NET Framework 4(免费)。

          使用它,您可以获得项目内存分配的时间表。或者您可以检查堆本身。它为您提供按类型分配的所有内容的列表。您可能会看到您过度分配的对象,从那里您可以调出该类型或该时间范围的分配图。这将显示哪些函数分配了这些对象。

          这里有一个random blog entry,其中包含一些屏幕截图和对 CLR Profiler 运行中的讨论。 (不完全是您将使用它做什么,但如果您以前从未使用过 CLR Profiler,这是一个有用的介绍。)

          然而:因为您使用的是 XNA,并且您通常必须非常努力地尝试真正让 C# 耗尽托管内存,您可能会用完 非托管内存。在您停止使用您创建的图形或声音对象之前,您是否没有调用Dispose()?这个a coupleof times的细节我已经讨论过了。

          因此请注意,如果 CLR Profiler 中显示了许多非常小的对象 - 它们实际上可能正在使用大量非托管内存。

          【讨论】:

          • 啊,谢谢!你是对的,你在一篇关于处理顶点缓冲区的帖子中提到,这似乎已经清除了它(好吧,我不能像以前那样重现它:))。不过,我还需要更改其他内容,我曾经为每个屏幕使用单独的内容管理器,我删除了该“功能”,现在只使用 xna 提供的内容管理器组件。在此之前,内存仍在使用中。那么,当个别内容类失去使用时,我是否也应该对它们进行处理?
          • 你应该 Dispose() IDisposable你创建的,在你摆脱它之前(ContentManagerIDisposable,所以你处理并且 it 将处理它加载的所有内容)。在this answer 的末尾,我有一整套关于使用 Content Manager 的信息。
          • 哦,很酷,但还有一个问题(关于您的回答),您说 ContentManager 可能共享非托管资源,例如纹理。这是有道理的,因为我记得如果我在一个内容管理器上调用 dispose,则纹理将无法再访问,并出现已被释放的错误。如果我处置内容管理器,创建一个新的并加载先前处置的纹理,它会正常工作还是我仍然会得到处置错误?如果另一个加载了纹理的内容管理器已经处理了它,是否可以在一个内容管理器中取消处理它?
          • @meds:ContentManager 的每个实例都“拥有”它Load()s 的所有内容。如果您尝试使用一个 ContentManager 多次加载一段内容,您总是会返回相同的对象(它保留一个列表)。但是,如果您有多个 ContentManager,它们彼此不了解,并且每个都需要在您第一次请求内容时加载自己的内容副本。在 ContentManager 上调用 DisposeUnload 将处理它已加载的所有内容,但对任何其他 ContentManager 或内容没有影响。
          【解决方案6】:

          我使用的是Mono profiler。它有多种选择;最简单的用法是

          mono --profile=log program.exe
          

          然后,在program.exe 退出后,它会留下一个分析器文件(默认为output.mlpd,并读取收集到的信息使用:

          mprof-report output.mlpd
          

          例如我做mprof-report output.mlpd | vim -

          默认情况下,它会收集一堆不同的信息。在输出的最开始(给定默认设置),您将看到按 «allocated» 列排序的函数表,例如一个片段:

          Allocation summary
            24      Bytes      Count  Average Type name
            25    7357392     306558       24 System.IntPtr
            26    6677904     139123       48 System.Collections.ArrayList.ArrayListEnumeratorSimple
            27    5842736     136185       42 Mono.Unix.Native.Syscall._pollfd[]
            28    3078176      49566       62 System.Byte[]
            29    2574504      38057       67 System.String
            30     908320      14803       61 System.Int32[]
            31     719984       5294      136 Mono.Globalization.Unicode.SortKeyBuffer
          

          我想不到它的优点:

          • 它是跨平台的,因此您可以在 GNU/Linux 和 Mac 上轻松分析 .net RAM 分配。
          • 它由 .net 的创建者和最大用户 — Microsoft 开发。早些时候它是由 Xamarin 开发的,但 MS 买了它们,现在它们在 Mono 主页上提到。

          【讨论】:

          • 有什么方法可以查看 Mono 分析器中的引用吗?我没有看到任何地方提到它。了解持有哪些引用对于尝试诊断丢失的内存很重要,因为无意中的引用持有数据,GC 无法清理。
          • 抱歉,无法对此发表评论。实际上,我已经有 2 年没有使用过类似 .net 的技术了(出于各种原因,我对此感到满意),所以我什至没有试验探查器的环境。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-12-11
          • 2020-08-05
          • 2023-03-21
          • 1970-01-01
          • 2017-07-03
          • 2015-08-01
          相关资源
          最近更新 更多