内存泄漏时的ThreadLocal< T>用于循环图内存、ThreadLocal、GT、LT

2023-09-03 07:23:12 作者:爱上你是我情非得已

我刚刚碰到关于垃圾收集器的这种怪异的行为 System.Threading.ThreadLocal< T> ,我无法解释。在正常情况下,的ThreadLocal< T> 实例会,当他们走出去的范围,即使他们没有妥善处理,除了在情况下他们的部分被垃圾收集一个循环对象图。

I just came across this weird 'behavior' of the Garbage Collector concerning System.Threading.ThreadLocal<T> that I can't explain. In normal circumstances, ThreadLocal<T> instances will be garbage collected when they go out of scope, even if they aren't disposed properly, except in the situation where they are part of a cyclic object graph.

下面的例子演示了这个问题:

The following example demonstrates the problem:

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();
    }
}

虽然没有叫处置不是很好的做法,但它的CLR的设计,以清理资源(通过调用终结器),最终,即使处置不叫。

Although not calling Dispose is not good practice, but it's the CLR's design to clean up resources (by calling the finalizer) eventually, even if Dispose is not called.

为什么的ThreadLocal 不同的表现在这方面,当不妥善处理的情况下,循环图的可导致内存泄漏?这是由设计?如果是的话,这哪里是记录?或者,这是CLR的GC一个错误?

Why does ThreadLocal behave differently in this regard and can cause memory leaks when not disposed properly in case of a cyclic graph? Is this by design? And if so, where is this documented? Or is this a bug in the CLR's GC?

(.NET 4.5下测试)。

(Tested under .NET 4.5).

推荐答案

原因是,你没有调用Dispose。垃圾收集器将只清理有终结在万不得已的对象。

The reason is that you're not calling Dispose. The garbage collector will only clean up objects that have finalizers as a last resort.