Fragmentation LOH – Mise à jour 2015

Il existe de nombreuses informations sur .NET LOH et cela a été expliqué dans divers articles. Cependant, il semble que certains articles manquent un peu de précision.

Information périmée

Dans sa réponse (2009), Brian Rasmussen, responsable de programme chez Microsoft , indique que la limite est de 85 000 octets. Il nous a également fait savoir qu’il existe un cas encore plus curieux de double[] avec une taille de 1000 éléments. Maoni Stephens (MSDN, 2008), membre de l’équipe CLR, a déclaré que la même limite était fixée à 85 000.

Dans les commentaires, Brian Rasmussen devient encore plus précis et nous permet de savoir qu’il peut être reproduit avec un byte[] de 85000 octets – 12 octets.

Mise à jour 2013

Mario Hewardt (auteur de «Advanced Windows Debugging») nous a dit en 2013 que .NET 4.5.1 peut désormais compacter le LOH également, si nous le lui demandons. Comme il est désactivé par défaut, le problème persiste sauf si vous en êtes déjà conscient.

Mise à jour 2015

Je ne peux plus reproduire l’exemple d’ byte[] . Avec un algorithme de force brute court, j’ai découvert que je devais soustraire 24 ( byte[84999-24] dans SOH, byte[85000-24] dans LOH):

  static void Main(ssortingng[] args) { int diff = 0; int generation = 3; while (generation > 0) { diff++; byte[] large = new byte[85000-diff]; generation = GC.GetGeneration(large); } Console.WriteLine(diff); } 

Je ne pouvais pas non plus reproduire la double[] déclaration double[] . Le forçage brutal me donne 10622 éléments comme frontière ( double[10621] dans SOH, double[10622] dans LOH):

  static void Main(ssortingng[] args) { int size = 85000; int step = 85000/2; while (step>0) { double[] d = new double[size]; int generation = GC.GetGeneration(d); size += (generation>0)?-step:step; step /= 2; } Console.WriteLine(size); } 

Cela se produit même si je comstack l’application pour les frameworks .NET plus anciens. Cela ne dépend pas non plus de la version Release ou Debug.

Comment expliquer les changements?

Le passage de 12 à 24 dans l’exemple d’ byte[] peut s’expliquer par le changement d’architecture de la CPU de 32 à 64 bits. Dans les programmes compilés pour x64 ou AnyCPU, la surcharge de .NET augmente de 2 * 4 octets (en-tête d’object de 4 octets + table de méthodes de 4 octets) à 2 * 8 octets (en-tête d’object de 8 octets + table de méthodes de 8 octets). De plus, le tableau a une propriété de longueur de 4 octets (32 bits) contre 8 octets (64 bits).

Pour l’exemple double[] , utilisez simplement une calculasortingce: 85 000 octets / 64 bits pour le type double = 10625 éléments, ce qui est déjà proche. Compte tenu de la surcharge .NET, le résultat est (85 000 octets – 24 octets) / 8 octets par double = 10622 en double. Il n’ya donc plus de traitement spécial de double[] .

Au fait, je n’avais jamais trouvé de démonstration valable de la fragmentation de LOH, j’en ai donc écrit une moi-même. Comstackz simplement le code suivant pour x86 et exécutez-le. Il inclut même des astuces de débogage.

Cela ne fonctionnera pas aussi bien avec une compilation en x64, car Windows pourrait augmenter la taille du fichier d’échange, de sorte que l’allocation ultérieure de 20 Mo de mémoire pourrait être à nouveau réussie.

 class Program { static IList small = new List(); static IList big = new List(); static void Main() { int totalMB = 0; try { Console.WriteLine("Allocating memory..."); while (true) { big.Add(new byte[10*1024*1024]); small.Add(new byte[85000-3*IntPtr.Size]); totalMB += 10; Console.WriteLine("{0} MB allocated", totalMB); } } catch (OutOfMemoryException) { Console.WriteLine("Memory is full now. Attach and debug if you like. Press Enter when done."); Console.WriteLine("For WinDbg, try `!address -summary` and `!dumpheap -stat`."); Console.ReadLine(); big.Clear(); GC.Collect(); Console.WriteLine("Lots of memory has been freed. Check again with the same commands."); Console.ReadLine(); try { big.Add(new byte[20*1024*1024]); } catch(OutOfMemoryException) { Console.WriteLine("It was not possible to allocate 20 MB although {0} MB are free.", totalMB); Console.ReadLine(); } } } }