.net memory-leaks thread-local

.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.