我刚刚遇到了这种奇怪的行为&#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下测试)。
答案 0 :(得分:0)
Microsoft的David Kean confirmed这实际上是一个错误。
答案 1 :(得分:-3)
原因是你没有调用Dispose。垃圾收集器只会清理具有终结器的对象作为最后的手段。