在循环图

时间:2015-10-16 14:12:05

标签: c# .net memory-leaks garbage-collection .net-4.5

我刚刚遇到了这种奇怪的行为&#39;有关System.Threading.ThreadLocal<T>的垃圾收集者,我无法解释。在正常情况下,ThreadLocal<T>实例在超出范围时将被垃圾收集,即使它们没有正确处理,除非它们是循环对象图的一部分。

以下示例演示了此问题:

public class Program
{
    public class B { public A A; }
    public class A { public ThreadLocal<B> LocalB; }

    private static List<WeakReference> references = new List<WeakReference>();

    static void Main(string[] args) {
        for (var i = 0; i < 1000; i++)
            CreateGraph();

        GC.Collect();
        GC.WaitForPendingFinalizers();
        GC.Collect();
        GC.WaitForPendingFinalizers();

        // Expecting to print 0, but it prints 1000
        Console.WriteLine(references.Count(c => c.IsAlive));
    }

    static void CreateGraph() {
        var a = new A { LocalB = new ThreadLocal<B>() };
        a.LocalB.Value = new B { A = a };
        references.Add(new WeakReference(a));

        // If either one of the following lines is uncommented, the cyclic
        // graph is broken, and the programs output will become 0.
        // a.LocalB = null;
        // a.LocalB.Value = null;
        // a.LocalB.Value.A = null;
        // a.LocalB.Dispose();
    }
}

虽然没有调用Dispose不是好习惯,但CLR的设计最终会清理资源(通过调用终结器),即使Dispose不是调用。

为什么ThreadLocal在这方面表现不同,如果在循环图中没有正确处理,会导致内存泄漏?这是设计的吗?如果是这样,这会记录在哪里?或者这是CLR的GC中的错误吗?

(在.NET 4.5下测试)。

2 个答案:

答案 0 :(得分:0)

Microsoft的David Kean confirmed这实际上是一个错误。

答案 1 :(得分:-3)

原因是你没有调用Dispose。垃圾收集器只会清理具有终结器的对象作为最后的手段。