Certains (au moins Mono et .NET) collecteurs de déchets ont une zone de mémoire à court terme qu'ils analysent souvent et une zone de mémoire secondaire qu'ils analysent moins souvent. Mono appelle cela une pépinière.
Pour savoir quels objets peuvent être supprimés, ils analysent tous les objets à partir des racines, de la pile et des registres et éliminent tous les objets qui ne sont plus référencés.
Ma question est de savoir comment ils empêchent toute la mémoire en cours d'utilisation d'être analysée à chaque collecte? En principe, la seule façon de savoir quels objets ne sont plus utilisés est de scanner tous les objets et toutes leurs références. Cependant, cela empêcherait le système d'exploitation d'échanger de la mémoire même si elle n'est pas utilisée par l'application et semble être une énorme quantité de travail à faire, également pour "Collection Nursery". Il ne semble pas qu'ils gagnent beaucoup en utilisant une pépinière.
Suis-je en train de manquer quelque chose ou le ramasse-miettes analyse-t-il réellement chaque objet et chaque référence chaque fois qu'il effectue une collecte?
la source
Réponses:
Les observations fondamentales qui permettent un ramassage des ordures générationnel pour éviter d'avoir à analyser tous les objets de génération plus ancienne sont:
Dans de nombreux frameworks GC, il est possible que le garbage collector marque des objets ou des parties de ceux-ci de telle sorte que la première tentative d'écriture sur eux déclenche un code spécial pour enregistrer le fait qu'ils ont été modifiés. Un objet ou une partie de celui-ci qui a été modifié, quelle que soit sa génération, doit être analysé dans la collection suivante, car il peut contenir des références à des objets plus récents. D'un autre côté, il est très courant qu'il y ait beaucoup d'objets plus anciens qui ne soient pas modifiés entre les collections. Le fait que les analyses de génération inférieure peuvent ignorer de tels objets peut permettre à ces analyses de se terminer beaucoup plus rapidement qu’elles ne le feraient autrement.
Notez, btw, que même si l'on ne peut pas détecter lorsque des objets sont modifiés et qu'il faudrait tout analyser à chaque passage du GC, la récupération de place générationnelle pourrait encore améliorer les performances de l'étape de "balayage" d'un collecteur de compactage. Dans certains environnements intégrés (en particulier ceux où il y a peu ou pas de différence de vitesse entre les accès séquentiels et aléatoires à la mémoire), le déplacement de blocs de mémoire est relativement coûteux par rapport au balisage de références. Par conséquent, même si la phase de «marquage» ne peut pas être accélérée à l'aide d'un collecteur générationnel, l'accélération de la phase de «balayage» peut être utile.
la source
Les GC auxquels vous faites référence sont des récupérateurs de génération . Ils sont conçus pour tirer le meilleur parti d'une observation connue sous le nom de «mortalité infantile» ou «hypothèse générationnelle», ce qui signifie que la plupart des objets deviennent inaccessibles très rapidement. Ils scannent en effet à partir des racines, mais ignorent tous les anciens objets . Par conséquent, ils n'ont pas besoin de scanner la plupart des objets en mémoire, ils ne scannent que les jeunes objets (au détriment de ne pas détecter les vieux objets inaccessibles, du moins pas à ce stade).
"Mais c'est faux", je vous entends crier, "les objets anciens peuvent et font référence à de jeunes objets". Vous avez raison, et il existe plusieurs solutions à cela, qui tournent toutes autour de l'acquisition de connaissances, rapidement et efficacement, quels objets anciens doivent être vérifiés et qui peuvent être ignorés en toute sécurité. Ils se résument à des objets d'enregistrement ou à de petites plages de mémoire (plus grandes que les objets, mais beaucoup plus petites que l'ensemble) qui contiennent des pointeurs vers les générations plus jeunes. D'autres ont décrit ceux-ci bien mieux que moi, je vais donc vous donner quelques mots-clés: marquage de carte, jeux mémorisés, barrières d'écriture. Il existe également d'autres techniques (y compris les hybrides), mais celles-ci englobent les approches courantes que je connais.
la source
Pour savoir quels objets de la pépinière sont encore en vie, le collecteur n'a qu'à scanner le jeu de racines et tous les anciens objets qui ont été mutés depuis la dernière collection , car un vieil objet qui n'a pas été récemment muté ne peut pas pointer vers un jeune objet . Il existe différents algorithmes pour maintenir ces informations à différents niveaux de précision (d'un ensemble exact de champs mutés à un ensemble de pages où une mutation peut s'être produite), mais ils impliquent généralement une sorte de barrière d'écriture : un code qui s'exécute sur chaque référence -mutation de champ typée qui met à jour la comptabilité du GC.
la source
La génération la plus ancienne et la plus simple de ramasse-miettes a effectivement analysé toute la mémoire et a dû arrêter tous les autres traitements pendant qu'elle le faisait. Les algorithmes ultérieurs se sont améliorés sur ce point de diverses manières - rendant la copie / numérisation incrémentielle ou exécutée en parallèle. La plupart des collecteurs d'ordures modernes séparent les objets en générations et gèrent soigneusement les pointeurs intergénérationnels afin que les nouvelles générations puissent être collectées sans déranger les plus anciennes.
Le point clé est que les garbage collector travaillent en étroite collaboration avec le compilateur et avec le reste du runtime pour maintenir l'illusion qu'il surveille toute la mémoire.
la source
Fondamentalement ... GC utilise des "compartiments" pour séparer ce qui est utilisé et ce qui ne l'est pas. Une fois qu'il le fait vérifier, il efface les choses qui ne sont pas utilisées et déplace tout le reste vers la 2e génération (qui est vérifié moins souvent que la 1re génération), puis déplace les choses qui sont encore en cours d'utilisation dans la 2e den à la 3e génération.
Donc, les choses de la 3e génération sont généralement des objets qui sont bloqués pour une raison quelconque, et GC n'y vérifie pas très souvent.
la source
L'algorithme habituellement utilisé par ce GC est le mark-and-sweep naïf
vous devez également être conscient du fait que ce n'est pas géré par le C # lui-même, mais par le soi-disant CLR .
la source