Tas des gros objets (LOH)
Zone séparée pour les objets de 85 000 octets ou plus. Elle n'est collectée qu'avec la génération 2.
Zone séparée pour les objets de 85 000 octets ou plus. Elle n'est collectée qu'avec la génération 2.
Les gros objets ne sont pas alloués en gen 0, car les copier coûterait cher. Le LOH fait logiquement partie de gen 2 : il n'est collecté que lorsqu'une collecte gen 2 a lieu, et il est normalement balayé, pas compacté (les blocs libres sont réutilisés et les voisins fusionnés). Le compactage peut être demandé avec GCSettings.LargeObjectHeapCompactionMode. Les grands tableaux temporaires (tampons, grandes chaînes, listes qui grossissent) remplissent donc vite le LOH, déclenchent des collectes gen 2 coûteuses et peuvent le fragmenter.
Remèdes : réutiliser les tampons (ArrayPool), éviter les collections qui grossissent par doublement, traiter les données en flux au lieu de les charger entières, et ajuster le seuil avec le paramètre GCLOHThreshold si besoin. C'est l'équivalent .NET des objets humongous de G1.
Documentation officielle : The large object heap on Windows (Microsoft Learn)
Compactage vs balayage
Après le marquage, le GC soit compacte les survivants, soit note seulement les trous libres (balayage). Il choisit à chaque collecte.
Le compactage rassemble les objets vivants, ce qui supprime la fragmentation et fait de l'allocation un simple avancement de pointeur, mais coûte du temps de copie et la mise à jour des références. Le balayage coûte moins cher mais laisse des trous, mis dans une liste libre et réutilisés.
Le GC compacte normalement les générations éphémères et décide pour gen 2 d'après la fragmentation ; le LOH est balayé par défaut. Une gen 2 qui ne compacte pas alors que le tas continue de grossir suggère une fragmentation. GCSettings.LargeObjectHeapCompactionMode permet de demander un compactage du LOH à la prochaine gen 2 bloquante.
Documentation officielle : Fundamentals of garbage collection (Microsoft Learn)
Épinglage
Fixer un objet à son adresse pour que le GC ne puisse pas le déplacer, comme l'exigent le code natif ou une instruction fixed.
Les objets épinglés (instructions fixed de C#, GCHandle de type Pinned, tampons utilisés par des E/S asynchrones) bloquent le compactage. Le GC doit les contourner, ce qui laisse des trous entre eux (fragmentation) et peut forcer gen 0 ou gen 1 à grandir ou à être promue.
Beaucoup d'épinglages de courte durée, comme dans le code réseau, coûtent peu. Quelques épinglages durables en gen 0 sont le vrai problème. Allouez une fois les tampons épinglés durables, dans le tas des objets épinglés (GC.AllocateArray avec pinned à true) ou dans le LOH, et réutilisez-les.
Tas des gros objets (LOH)