.net - ThreadLocal<> y pérdida de memoria
memory-leaks thread-local (2)
Al ejecutar .Net 4.5 DP, no veo ninguna diferencia entre presionar R o no en su aplicación. Si realmente hubo una pérdida de memoria en 4.0, parece que fue solucionado.
(4.5 es una actualización en el lugar, por lo que no puedo probar 4.0 en la misma computadora, lo siento).
.Net 4. ThreadLocal <> implementa IDisposable. Pero parece que llamar a Dispose () realmente no libera referencias para enhebrar objetos locales que se están reteniendo.
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();
}
}
}
Enhebre 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á enraizado por GCHandle y se asignó 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 un defecto en el diseño ThreadLocal <>. El truco con el almacenamiento de todos los objetos asignados para una mayor limpieza es feo. ¿Alguna idea sobre cómo solucionar esto?
La memoria probablemente ha sido recogida de basura, pero el proceso de CLR no se ha liberado aún. Tiende a retener la memoria asignada por un tiempo en caso de que la necesite más tarde, por lo que no tiene que hacer una asignación de memoria costosa.