Ich bin gerade auf dieses seltsame "Verhalten" des Garbage Collector bezüglich System.Threading.ThreadLocal<T>
gestoßen, das ich nicht erklären kann. Unter normalen Umständen werden ThreadLocal<T>
Instanzen als Garbage Collected erfasst, wenn sie außerhalb des Gültigkeitsbereichs liegen, auch wenn sie nicht ordnungsgemäß entsorgt werden, außer in den Fällen, in denen sie Teil eines zyklischen Objektdiagramms sind.Speicherleck, wenn ThreadLocal <T> in zyklischen Graphen verwendet wird
Das folgende Beispiel zeigt das 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();
}
}
Obwohl nicht Dispose
nicht gute Praxis nennen, aber es ist das Design der CLR Ressource zu bereinigen (durch die Finalizerthread Aufruf) schließlich, auch wenn Dispose
nicht aufgerufen .
Warum verhält sich ThreadLocal
in dieser Hinsicht anders und kann Speicherlecks verursachen, wenn sie im Falle eines zyklischen Graphen nicht ordnungsgemäß entsorgt werden? Ist das Absicht? Und wenn ja, wo ist das dokumentiert? Oder ist das ein Fehler im GC der CLR?
(Getestet unter .NET 4.5).
[David Kean] (https://twitter.com/davkean) bestätigte, dass dies ein Fehler ist. – Steven
[Klicken Sie auf] (https://mobile.twitter.com/davkean/status/655067698511024128). – Steven