【问题标题】:Get active references to an object获取对对象的活动引用
【发布时间】:2010-12-19 17:00:31
【问题描述】:

我正在寻找一个托管/非托管 API,它可以让我找到哪些对象引用了另一个对象,并有可能防止它被垃圾收集。

这样的 API 可能如下所示:

var foo = new Foo();
var bar = new Bar();
bar.Foo = foo;

var references = GC.GetReferencesTo(foo);
// references is an array that contains bar

我知道分析器可以用于此,但我想将其作为单元测试的一部分。是否有我可以使用的托管或非托管 API?

【问题讨论】:

标签: c# .net garbage-collection


【解决方案1】:

非托管 dll SOS (Son Of Strike) 提供了实现这一点的方法,尽管我不认为它具有重要的脚本支持,也没有提供通过单个命令实现这一点的简单方法。您必须自省变量的地址,检查变量的所有 gcroots(显然包括堆栈)并处理剩余部分。

我建议,如果你想证明一个对象没有被引用,一种更简单的技术是(暂时)使其可终结,确保它不再在堆栈中被引用您的单元测试,然后通过GC.Collect() 强制进行几次垃圾收集,然后使用GC.WaitForPendingFinalizers()

您的 finalize 方法可以设置一个静态布尔标志,然后您的单元测试可以断言这是真的。

我会在没有进一步解释的情况下质疑它的实用性,但这可能是证明单元测试中不存在悬空引用的最简单方法。

【讨论】:

  • 我目前对对象使用 Wea​​kReference,后跟 GC.Collect/WFPF,然后断言引用为 null 以告诉我它是否已被清除。我这样做是作为单元测试的一部分,以确保我的 API 不会导致泄漏。但作为“断言​​”输出的一部分,我想打印出哪些对象使其保持活动状态,以免我跳入分析器。
  • 我严重怀疑这是否可能以自动方式实现,而不需要大量工作来自己公开 SOS 功能,除非 MS 中的某个人已经这样做了。我建议最好的办法是考虑编写基于 SOS 的东西,将整个搜索自动化到一个命令中,这样你就可以从调试器中的失败测试中触发它。请注意,SOS 不是独立工具,它完全依赖于附加的兼容调试器...
  • 如果你真的想尝试破解它,这里是一个凝视点:blogs.microsoft.co.il/blogs/sasha/archive/2009/08/17/…
  • 也看看有些人在包装 SOS 时有什么:old.thinktecture.com/SOSAssist/Screenshots.htm
【解决方案2】:

.NET Profiler 使用profiling API 来跟踪对象图。您可能对回调方法ObjectReferencesRootReferences 以及ObjectAllocated 特别感兴趣。前两个方法将在每次垃圾收集后被调用以覆盖整个活动对象图,因此仅拦截它们就足以重建该图,然后以您想要的任何方式对其进行分析。

This article 解释了如何将所有部分组合在一起。

【讨论】:

  • 该链接中缺少文章
【解决方案3】:

还记得 .NET 使用 traced garbage collection 所以其他对象引用的对象 - 例如。对象图 - 如果您的应用不再引用它们中的任何一个,GC 仍将清理它们;它比.NET 之前的经典引用计数垃圾收集算法要聪明得多。这意味着即使您找到了一种方法来查明对某个对象的所有引用,其中一些可能并不重要并且可能不需要处理。

这并没有直接回答如何找到对一个对象的所有引用,但确实让读者意识到,当您通过工具或实用程序找到所有引用时,有些事情不需要在 .NET 中不再修复 - 例如经典的循环引用,因此您可能必须寻找一些巧妙的方法来了解在尝试修复内存泄漏时应该忽略什么。

此解释仅适用于托管代码方案。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-12-08
    • 1970-01-01
    • 2013-08-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-23
    • 1970-01-01
    相关资源
    最近更新 更多