Pourquoi Large Object Heap et pourquoi s’en soucie-t-on?

J’ai lu à propos des générations et du tas d’objects volumineux. Mais je n’arrive toujours pas à comprendre quelle est la signification (ou le bénéfice) d’avoir un tas d’objects volumineux?

Qu’est-ce qui aurait pu mal se passer (en termes de performance ou de mémoire) si CLR venait de s’appuyer sur la génération 2 (considérant que le seuil pour Gen0 et Gen1 est petit pour gérer les objects volumineux) pour stocker de gros objects?

    Un ramasse-miettes ne se débarrasse pas seulement des objects non référencés, il compresse également le tas. C’est une optimisation très importante. Cela ne rend pas seulement l’utilisation de la mémoire plus efficace (pas de trous inutilisés), cela rend le cache du processeur beaucoup plus efficace. Le cache est très important sur les processeurs modernes, ils sont d’un ordre de grandeur plus rapide que le bus mémoire.

    Le compactage se fait simplement en copiant des octets. Cela prend cependant du temps. Plus l’object est gros, plus le coût de la copie est susceptible d’être supérieur aux améliorations possibles de l’utilisation du cache du processeur.

    Donc, ils ont couru un tas de repères pour déterminer le seuil de rentabilité. Et est arrivé à 85 000 octets comme sharepoint coupure où la copie n’améliore plus la perf. Avec une exception spéciale pour les tableaux de double, ils sont considérés comme «grands» lorsque le tableau contient plus de 1000 éléments. C’est une autre optimisation pour le code 32 bits, l’allocateur de tas de grand object a la propriété spéciale d’allouer de la mémoire aux adresses alignées sur 8, contrairement à l’allocateur de générations régulier qui n’alloue que sur 4. , lire ou écrire un double mal aligné est très coûteux. Bizarrement, les rares informations de Microsoft ne mentionnent jamais les tableaux de long, pas sûr de ce qui se passe avec ça.

    Fwiw, il y a beaucoup d’angoisse de programmeur à propos du tas d’objects volumineux qui ne sont pas compactés. Cela se produit invariablement quand ils écrivent des programmes qui consumnt plus de la moitié de l’espace d’adressage disponible. Suivi en utilisant un outil comme un profileur de mémoire pour savoir pourquoi le programme a bombardé même s’il y avait encore beaucoup de mémoire virtuelle inutilisée disponible. Un tel outil montre les trous dans LOH, des morceaux de mémoire inutilisés où un grand object vivait auparavant mais des déchets collectés. Tel est le prix inévitable de la LOH, le trou ne peut être réutilisé que par une allocation pour un object de taille égale ou inférieure. Le vrai problème est de supposer qu’un programme devrait être autorisé à consumr toute la mémoire virtuelle à tout moment.

    Un problème qui disparaît complètement en exécutant simplement le code sur un système d’exploitation 64 bits. Un processus 64 bits dispose de 8 téraoctets d’espace d’adressage de mémoire virtuelle, soit trois fois plus qu’un processus 32 bits. Vous ne pouvez pas manquer de trous.

    En bref, le LOH rend le code plus efficace. Au prix de l’utilisation de l’espace d’adressage de mémoire virtuelle disponible moins efficace.


    UPDATE, .NET 4.5.1 prend désormais en charge le compactage de la propriété LOH, GCSettings.LargeObjectHeapCompactionMode . Attention aux conséquences s’il vous plaît.

    Si la taille de l’object est supérieure à une valeur épinglée (85 000 octets dans .NET 1), le CLR le place dans le tas d’objects volumineux. Cela optimise:

    1. Affectation d’objects (les petits objects ne sont pas mélangés avec de gros objects)
    2. Collecte des ordures ménagères (LOH collectée uniquement sur GC complet)
    3. Défragmentation de la mémoire (LOH n’est jamais rarement compacté)

    La différence essentielle entre SOH (Small Object Heap) et LOH (Large Object Heap) est que la mémoire dans SOH est compactée lorsqu’elle est collectée, tandis que LOH ne l’est pas, comme l’illustre cet article . Le compactage de gros objects coûte cher. Comme dans les exemples de l’article, le déplacement d’un octet en mémoire nécessite 2 cycles, puis le compactage d’un object de 8 Mo dans un ordinateur de 2 GHz nécessite 8 ms, ce qui représente un coût important. Considérer que les objects volumineux (les tableaux dans la plupart des cas) sont assez courants dans la pratique, je suppose que c’est la raison pour laquelle Microsoft épingle de grands objects en mémoire et propose LOH.

    BTW, selon ce post , LOH ne génère généralement pas de problèmes de fragment de mémoire.

    Le principe est qu’il est peu probable qu’un processus crée un grand nombre d’objects volumineux de courte durée, de sorte que le CLR alloue des objects volumineux à un segment distinct sur lequel il exécute GC selon une planification différente du segment de mémoire normal. http://msdn.microsoft.com/en-us/magazine/cc534993.aspx

    Je ne suis pas un expert du CLR, mais j’imagine qu’avoir un tas dédié aux gros objects peut empêcher des balayages GC inutiles des tas de générations existants. Allouer un object volumineux nécessite une quantité importante de mémoire libre contiguë . Afin de fournir cela à partir des “trous” dispersés dans les tas de générations, vous auriez besoin de compactions fréquentes (qui ne sont effectuées qu’avec les cycles GC).