在运行时查找对象的引用

er-*_*r-v 21 .net c# 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);
Run Code Online (Sandbox Code Playgroud)

我怎样才能找到它仍然有哪些参考?为什么不收集?

Ian*_*ose 15

尝试使用内存分析器(例如蚂蚁)它会告诉你什么使对象保持活着.试图第二次猜测这类问题非常困难.

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

市场上有很多其他的内存分析器(例如.NET Memory Profiler),大多数都有免费试用版,但是我发现Red-Gate工具很容易使用,所以最好先尝试一下.


Pie*_*aud 5

我解决了 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");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

.load C:\Windows\Microsoft.NET\Framework\v4.0.30319\sos.dll
Run Code Online (Sandbox Code Playgroud)

这将加载SOS.dll扩展。

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

!do 8AB0CD40
!GCRoot -all 8AB0CD40
Run Code Online (Sandbox Code Playgroud)

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

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