Właśnie natknąłem się na dziwne "zachowanie" Garbage Collectora dotyczące System.Threading.ThreadLocal<T>
, którego nie potrafię wyjaśnić. W normalnych okolicznościach instancje ThreadLocal<T>
będą gromadzone podczas usuwania z zakresu, nawet jeśli nie zostaną prawidłowo rozmieszczone, z wyjątkiem sytuacji, gdy są częścią cyklicznego wykresu obiektów.Przeciek pamięci, gdy ThreadLocal <T> jest używany w cyklicznym wykresie
Poniższy przykład demonstruje 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();
}
}
Chociaż nie nazywając Dispose
nie jest dobra praktyka, ale jest to projekt CLR, aby oczyścić zasoby (poprzez wywołanie finalizatora) w końcu, nawet jeśli Dispose
nie nazywa .
Dlaczego ThreadLocal
zachowuje się inaczej pod tym względem i może powodować wycieki pamięci, jeśli nie zostaną prawidłowo usunięte w przypadku wykresu cyklicznego? Czy to według projektu? A jeśli tak, to gdzie jest to udokumentowane? Czy jest to błąd w GC CLR?
(Przetestowano pod .NET 4.5).
[David Kean] (https://twitter.com/davkean) potwierdził, że jest to błąd. – Steven
[Kliknij] (https://mobile.twitter.com/davkean/status/655067698511024128). – Steven