2011-09-27 3 views
7

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

+1

Ê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 –

+0

Il vaut mieux utiliser 'GC.GetTotalMemory (true)' pour mesurer la mémoire, mais cela ne garantit pas non plus que tout sera collecté. – Ray

+1

Imprimé GC.GetTotalMemory(). Il donne 335607644 quand je n'ai pas zéro ** data ** field et 63268 quand je le fais. – SergeyS

Répondre

1

La mémoire a probablement été récupérée mais le processus CLR ne l'a pas encore abandonné. Il a tendance à conserver un peu de mémoire allouée au cas où il en aurait besoin plus tard, il n'a donc pas à faire une allocation de mémoire coûteuse.

+2

GC se comporte différemment si le champ 'data' est mis à zéro. Plus Memory Profiler montre que l'objet ThreadLocalData est en fait quelque part originaire de ThreadLocal <> internes. – SergeyS

1

En cours d'exécution sur .Net 4.5 DP, je ne vois aucune différence entre appuyer sur R ou pas dans votre application. S'il y avait effectivement une fuite de mémoire dans 4.0, il semble qu'il a été réparé.

(4.5 est une mise à jour en place, donc je ne peux pas tester 4.0 sur le même ordinateur, désolé.)