在运行时查找对象的引用

时间:2009-09-24 14:07:49

标签: c# .net garbage-collection

我有一个永生的物体。我删除了所有可以看到的引用,使用它之后,它仍然没有收集。它的生命周期非常复杂,所以我不能确定所有引用都已被清除。

if ( container.Controls.Count > 0 )
{ 
    var controls = new Control[ container.Controls.Count ];
    container.Controls.CopyTo( controls, 0 );

    foreach ( var control in controls ) 
    { 
         container.Controls.Remove( control );
         control.Dispose();
    }

    controls = null; 
}

GC.Collect();
GC.Collect(1);
GC.Collect(2);
GC.Collect(3);

我如何找出它仍有哪些参考?为什么不收集?

6 个答案:

答案 0 :(得分:15)

尝试使用内存分析器,(例如ants)它会告诉您保持对象存活的原因。试图猜测这类问题非常困难。

Red-gate提供了14天的试用期,应该有足够的时间来解决这个问题并决定内存分析器是否为您提供长期价值。

市场上有其他内存分析器很多(例如.NET Memory Profiler)大多数都有免费试用,但我发现Red-Gate工具很容易使用,所以倾向于首先尝试它们。

答案 1 :(得分:4)

您必须使用WindbgSosex扩展名。

!DumpHeap!GCRoot命令可以帮助您识别实例以及使其保持活动状态的所有剩余引用。

答案 2 :(得分:4)

我解决了SOS扩展的类似问题(显然不再适用于Visual Studio 2013,但在旧版本的Visual Studio中可以正常工作)。

我使用以下代码来获取我想要跟踪引用的对象的地址:

public static string GetAddress(object o)
{
    if (o == null)
    {
        return "00000000";
    }
    else
    {
        unsafe
        {
            System.TypedReference tr = __makeref(o);
            System.IntPtr ptr = **(System.IntPtr**) (&tr);
            return ptr.ToString ("X");
        }
    }
}

然后,在Visual Studio 2012立即窗口中,在调试器中运行时,键入:

.load C:\Windows\Microsoft.NET\Framework\v4.0.30319\sos.dll

将加载SOS.dll扩展名。

然后,您可以使用GetAddress(x)获取对象的十六进制地址(例如8AB0CD40),然后使用:

!do 8AB0CD40
!GCRoot -all 8AB0CD40

转储对象并查找对象的所有引用。

请记住,如果GC运行,它可能会更改对象的地址。

答案 3 :(得分:3)

我一直在使用.NET Memory Profiler对我们的一个项目进行一些严肃的内存分析。这是一个了解应用程序内存管理的好工具。我没有得到这个信息的报酬:)但它只是帮了我很多。

答案 4 :(得分:2)

.NET中的垃圾收集不是计数方案(例如COM),而是标记和扫描实现。基本上,GC在需要时会“随机”运行,因此对象的收集不是确定性的。

但是,您可以手动触发集合(GC.Collect()),但可能必须等待终结器运行(GC.WaitForPendingFinalizers())。但是,不鼓励在生产应用程序中执行此操作,因为它可能会影响内存管理的效率(GC运行过于频繁,或等待终结器运行)。如果对象仍然存在,它实际上仍然在某处有一些实时引用。

答案 5 :(得分:0)

未收集它,因为您尚未删除对它的所有引用。只有在应用程序中没有根目录时,GC才会标记要收集的对象。

您使用什么方法检查GC以查看它是否收集了您的对象?