.Net 4. ThreadLocal > implémente IDisposable. Mais il semble qu'appeler Dispose() ne libère pas réellement de références aux objets locaux threads détenus.ThreadLocal <> et fuites de mémoire
Ce code reproduit le problème:
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();
}
}
}
discussion banques de données locales une référence à un grand objet. GC ne collecte pas ces objets volumineux si les références ne sont pas annulées manuellement. J'ai utilisé le Gestionnaire des tâches pour observer la consommation de mémoire. Je cours également le profileur de mémoire. J'ai fait un instantané après la collecte des ordures. Le profileur a montré que l'objet de fuite est enraciné par GCHandle et a été attribué avec ici:
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
qui semble être une faille dans ThreadLocal <> conception. L'astuce avec le stockage de tous les objets alloués pour un nettoyage supplémentaire est moche. Des idées sur la façon de contourner cela?
Êtes-vous en debug ou release pour cela? aussi, le gestionnaire de tâches n'est pas vraiment très utilisable pour ce que vous mesurez –
Il vaut mieux utiliser 'GC.GetTotalMemory (true)' pour mesurer la mémoire, mais cela ne garantit pas non plus que tout sera collecté. – Ray
Imprimé GC.GetTotalMemory(). Il donne 335607644 quand je n'ai pas zéro ** data ** field et 63268 quand je le fais. – SergeyS