【问题标题】:Debugging .NET memory leaks - how to know what is holding a reference to what?调试 .NET 内存泄漏 - 如何知道什么持有对什么的引用?
【发布时间】:2012-06-21 07:47:36
【问题描述】:

我正在开发一个似乎存在内存泄漏的 .NET 应用程序。我知道教科书的答案,应该取消订阅事件,应该处置一次性物品等等......

我有一个可以重现错误的测试工具。在某个类的终结器中,我写到控制台

public class Foo
{
   // Ctor
   public Foo()
   {
   }

   ~public Foo()
   {
       Console.WriteLine("Foo Finalized");
   }
}

在测试工具中,我创建了一个 Foo 实例(它反过来创建并与数百个其他类型交互),然后删除它并调用垃圾收集器。

我发现永远不会调用 Foo 终结器。我有一个与此设置类似的课程,最终确定为控制测试。

所以我的问题是这样的:

我如何才能确定使用商业或开源工具究竟是什么 是否持有对 Foo 的引用?

我拥有 dotTrace Memory profiler 的专业许可,但无法从帮助文件中了解如何使用它。

更新:我现在使用的是dotMemory 4.0,它是(很好,但无法使用)dotTrace Memory 3.5 的后续版本。

【问题讨论】:

  • 如果你已经有一个分析器学习使用它 - 我没有经常使用 dotTrace 但我其他两个在使用模式中相似并且也需要一些“入门”
  • @CarstenKönig 对此 +1,我现在正在 dotTrace 中挖掘。发现我可以通过命名空间找到未收集的对象列表(所以找到了我的违规 Foo)以及对它们的引用,再次按命名空间(数百个)过滤,现在通过可疑事件处理程序列表等...跨度>
  • @CarstenKönig 实际上是使用 dotTrace 找到的!它确实具有从 gcroot -> Foo 向下钻取图形的功能,只是有点难以找到。干杯:)

标签: c# memory-leaks garbage-collection


【解决方案1】:

【讨论】:

    【解决方案2】:

    SOS debugger 扩展名中有一个look(它是免费的,可以在Visual Studio 中使用)。

    您可能会发现thisthis 有助于入门。

    如果您已成功设置 SOS(有时这可能会很棘手),那么知道什么包含对什么的引用就那么简单

    // load sos
    .load sos
    // list of all instances of YourTypeName in memory with their method tables
    !DumpHeap -type YourTypeName  
    // put here the method table displayed by the previous command
    // it will show you the memory address of the object
    !DumpHeap -mt 07f66b44              
    // displays information about references the object at the specified address
    !GCRoot 02d6ec94
    

    【讨论】:

    • 谢谢你,我一定会试一试的。如果有关于 dotTrace 的好教程会很好,但遗憾的是没有!
    【解决方案3】:

    终结器不是确定性地调用的,所以要小心使用它以可靠的方式跟踪事物。如果您删除终结器并改用WeakReference<Foo>,您应该能够确定对象是否被收集。

    所有内存分析器都应该能够找到诸如此类的问题,但难度各不相同。我个人使用过 ANTS,它很容易使用,但不是免费的。它将帮助您显示 Foo 实例的引用图,从 GC 根对象开始。看到这张图,通常很容易发现谁在持有参考。

    【讨论】:

    • 我已经厌倦了 3 个不同的分析器,无法查看我所看到的是否是泄漏以及泄漏的来源。我知道所有正常的陷阱,只需要一个可以成为橡皮鸭,对内存分配和保留有相当了解的人stackoverflow.com/questions/61575338/…我相信我看到的是.net核心代码中的一个错误,而不是我的自己的,但很难说,因为有大约 0 条线索。所以可能会陷入死胡同。
    【解决方案4】:

    您可以使用内存分析器来识别内存泄漏。这里有一些,

    MemProfiler

    ANTS Profiler

    【讨论】:

    • 投票支持 ANTS Profiler。在一个相当大的企业应用程序中,设法找到了 3 年未检测到的巨大资源泄漏。
    【解决方案5】:

    首先你不应该使用终结器,因为:

    Finalize 操作有以下限制:

    • 垃圾回收期间终结器执行的确切时间 未定义。资源不保证随时释放 具体时间,除非调用 Close 方法或 Dispose 方法。

    • 不保证两个对象的终结器在任何 特定的顺序,即使一个对象引用另一个对象。也就是说,如果 对象 A 具有对对象 B 的引用,并且都具有终结器 Object 当对象 A 的终结器启动时,B 可能已经完成。

    • 运行终结器的线程未指定。

    引用自:http://msdn.microsoft.com/en-us/library/system.object.finalize.aspx
    我建议改用 Dispose 方法。

    其次,任何内存分析器都应该能够找到保存这些引用的内容。我个人使用的是 ANTS Profiler,它是一个非常好的工具,并且有相当丰富的文档。您可以尝试阅读此文档:http://downloads.red-gate.com/HelpPDF/ANTS_Memory_Profiler/InstanceCategorizer.pdf 实例分类器显示从对象集到 GC 根的引用链。

    【讨论】:

    • 虽然您完全正确,但在某些情况下确实有必要使用 Finalizer。假设您创建了一个自定义控件来渲染您重新分配给最终用户的 3D 表面。一旦不再使用控件,最终用户就不太可能在控件上调用 .Dispose()。他们希望它“正常工作”。此外,WPF 垃圾有时会在视图被模板化并在选项卡控件中以及选项卡退出和退出时收集视图。在这些情况下,它不可能调用 .Dispose 并且 Finalizer 是通过直接为您调用 Dispose 来释放非托管资源的最佳选择,如果还没有
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-02-01
    • 2012-01-01
    • 1970-01-01
    • 2013-10-16
    • 1970-01-01
    • 2011-03-05
    • 1970-01-01
    相关资源
    最近更新 更多