.Net 4. ThreadLocal <> implementuje IDisposable. Wydaje się jednak, że wywołanie metody Dispose() w rzeczywistości nie zwalnia z odwoływania się do utrzymywanych lokalnych obiektów wątku.ThreadLocal <> i wyciek pamięci
Ten kod reprodukuje problem:
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Linq;
using System.Threading;
namespace ConsoleApplication2
{
class Program
{
class ThreadLocalData
{
// Allocate object in LOH
public int[] data = new int[10 * 1024 * 1024];
};
static void Main(string[] args)
{
// Stores references to all thread local object that have been created
var threadLocalInstances = new List<ThreadLocalData>();
ThreadLocal<ThreadLocalData> threadLocal = new ThreadLocal<ThreadLocalData>(() =>
{
var ret = new ThreadLocalData();
lock (threadLocalInstances)
threadLocalInstances.Add(ret);
return ret;
});
// Do some multithreaded stuff
int sum = Enumerable.Range(0, 100).AsParallel().Select(
i => threadLocal.Value.data.Sum() + i).Sum();
Console.WriteLine("Sum: {0}", sum);
Console.WriteLine("Thread local instances: {0}", threadLocalInstances.Count);
// Do our best to release ThreadLocal<> object
threadLocal.Dispose();
threadLocal = null;
Console.Write("Press R to release memory blocks manually or another key to proceed: ");
if (char.ToUpper(Console.ReadKey().KeyChar) == 'R')
{
foreach (var i in threadLocalInstances)
i.data = null;
}
// Make sure we don't keep the references to LOH objects
threadLocalInstances = null;
Console.WriteLine();
// Collect the garbage
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
Console.WriteLine("Garbage collected. Open Task Manager to see memory consumption.");
Console.Write("Press any key to exit.");
Console.ReadKey();
}
}
}
wątku lokalnych magazynów danych odniesienie do dużego obiektu. GC nie zbiera tych dużych obiektów, jeśli odniesienia nie są zerowane ręcznie. Użyłem Menedżera zadań do obserwowania zużycia pamięci. Prowadzę również profiler pamięci. Zrobiłem migawkę po zebraniu śmieci. Profiler wykazało, że wyciekły obiekt jest zakorzenione przez GCHandle i przeznaczono z tutaj:
mscorlib!System.Threading.ThreadLocal<T>.GenericHolder<U,V,W>.get_Boxed()
mscorlib!System.Threading.ThreadLocal<T>.get_Value()
ConsoleApplication2!ConsoleApplication2.Program.<>c__DisplayClass3.<Main>b__2(int) Program.cs
To wydaje się być wadą ThreadLocal <> design. Sztuczka z przechowywaniem wszystkich przydzielonych obiektów do dalszego oczyszczania jest brzydka. Wszelkie pomysły, jak to obejść?
Czy jesteś w debugowania lub uwolnienia do tego? ponadto, menedżer zadań nie jest zbyt użyteczny dla tego, co mierzysz. –
Lepiej używać "GC.GetTotalMemory (true)", aby zmierzyć pamięć, ale to też nie gwarantuje, że wszystko zostanie zebrane. – Ray
Wydrukowano GC.GetTotalMemory(). Daje to 335607644, gdy nie zeruję ** pola danych ** i 63268, kiedy to robię. – SergeyS