如何在C#中查找内存使用情况?

7
我有一个正在运行的C# XNA WP7项目,发现在屏幕切换期间会消耗内存并且没有释放,最终导致内存不足异常。
我已经找了很久,但一直找不到内存去哪了。
有没有办法找出内存使用的位置及为什么没有被返还给设备呢?
谢谢任何帮助!
6个回答

6

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

使用它可以获得项目内存分配的时间轴。或者您可以直接检查堆(heap)本身。它会给出按类型分配的所有对象列表。您可能会看到您正在过度分配的对象,然后您可以为该类型或时间范围引出一个分配图表。这将显示分配这些对象的函数。

这里有一篇随机博客文章,其中包含一些 CLR Profiler 的截图和讨论。(虽然不是完全与您所用相同,但如果您以前从未使用过 CLR Profiler,则会有用的介绍。)

然而:由于您使用的是 XNA,并且通常需要尝试很多次才能使 C#耗尽托管内存,因此您可能会耗尽了非托管内存。是否有某个地方在您停止使用已创建的图形或声音对象之前没有调用Dispose()?我已经在这里多次讨论了这个问题的细节。

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


哦,谢谢!你是对的,你在一篇帖子中提到了处理顶点缓冲区,这似乎已经解决了问题(我无法再像之前那样重现它 :))。但还有另一件事我必须改变,我过去每个屏幕都使用一个单独的内容管理器,现在我删除了这个“特性”,直接使用XNA提供的组件。在此之前,内存仍在被使用。所以当它们失去用途时,我也应该调用单个内容类的dispose吗? - meds
1
在摆脱某个对象之前(ContentManagerIDisposable的,所以需要处理它),你应该Dispose()任何你创建的IDisposable 对象。我在此答案的结尾有一个使用内容管理器的完整信息栈。注意,ContentManager会处理加载的所有内容对象。 - Andrew Russell
哦,好的,不错。但我还有一个问题(关于你的答案),你说ContentManagers可以共享未受管理的资源,例如纹理。这很有道理,因为我记得如果我在一个content manager上调用dispose,那么一个纹理将不再可访问,出现已释放的错误。如果我释放了content manager,创建一个新的,并加载一个纹理,之前释放的会正常工作吗,还是仍然会出现已释放的错误?如果另一个content manager已经释放了其中载入的内容,是否可能在一个contentmanager中取消释放纹理? - meds
@meds:每个ContentManager实例“拥有”它Load()的所有内容。如果您尝试使用一个ContentManager多次加载某个内容,您总是会得到相同的对象(它会保留列表)。但是,如果您有多个ContentManagers,则它们彼此不知道,并且每个ContentManager在您第一次请求时都需要加载自己的内容副本。在ContentManager上调用DisposeUnload将处理它加载的所有内容,但对其他ContentManagers或内容没有影响。 - Andrew Russell

2
你可以尝试使用Redgate的ANTS内存分析器(但需要付费),我不确定它是否适用于WP7,但它是针对C#开发的。

有一个免费试用版,所以你可以试用一下来帮助定位问题。


1

Eqatec有一个可以与WP7一起使用的分析器。它不是内存分析器,但我建议您尝试一下查看其显示了什么。这可能会帮助您指向正确的方向。


1

coding4fun toolkit 包含一个内存计数器,可以帮助跟踪应用程序的内存使用情况。这里是文档和一个文章演示其使用。


1

.NET 4.0的CLR分析器现已推出。请查看我的回答。 - Andrew Russell

0
我使用的是Mono profiler。它有各种选项;最简单的用法是:
mono --profile=log program.exe

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

例如,我执行mprof-report output.mlpd | vim -

默认情况下,它会收集许多不同的信息。在输出的开头处(给定默认设置),您将看到按“分配”列排序的函数表,例如:

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的创造者和最大用户——微软开发。早些时候它是由Xamarin开发的,但是微软收购了他们,现在在主要的Mono页面上提到了他们。

有没有办法在Mono分析器中查看引用?我没有在任何地方看到它被提到。了解正在保留哪些引用对于尝试诊断由于意外引用而无法清理的丢失内存对GC非常重要。 - JamesHoux
抱歉,我无法对此发表评论。实际上,我已经有两年没有使用类似 .net 的技术了(出于各种原因,我很高兴),所以甚至没有环境来尝试使用分析器。 - Hi-Angel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接