.Net 4. ThreadLocal <> implementa IDisposable. Pero parece que llamar a Dispose() realmente no libera referencias para enhebrar objetos locales que se están reteniendo.ThreadLocal <> y fuga de memoria
Este código reproduce el problema:
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();
}
}
}
rosca almacenes de datos locales una referencia a un objeto grande. GC no recoge estos objetos grandes si las referencias no se anulan manualmente. Usé el Administrador de tareas para observar el consumo de memoria. También ejecuto el generador de perfiles de memoria. Hice una instantánea después de recoger la basura. El generador de perfiles mostró que el objeto filtrado está arraigado por GCHandle y se asignó con aquí:
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
Eso parece ser una falla en ThreadLocal <> diseño. El truco con el almacenamiento de todos los objetos asignados para una mayor limpieza es feo. ¿Alguna idea sobre cómo solucionar esto?
¿Estás en depuración o la versión de este? Además, el administrador de tareas no es muy útil para lo que está midiendo –
. Es mejor utilizar 'GC.GetTotalMemory (true)' para medir la memoria, pero eso tampoco garantiza que se recopile todo. – Ray
GC.GetTotalMemory impreso(). Da 335607644 cuando no campo cero ** datos ** y 63268 cuando lo hago. – SergeyS