2009-09-23 4 views
7

Dans mon application, il existe un moment spécifique où un certain nombre de grands objets sont tous libérés en même temps. A ce moment je voudrais faire une collecte des ordures sur spécifiquement le gros tas d'objets (LOH).GC.Collect sur le segment de mémoire de la génération 2 et des objets volumineux

Je suis conscient que vous ne pouvez pas faire cela, vous devez appeler GC.Collect(2) parce que le GC est seulement appelé sur le LOH quand il fait une collection de génération 2. Cependant, je l'ai lu dans la documentation appelant GC.Collect(2) serait encore exécuter un GC sur les générations 1 et 0.

Est-il possible de forcer le GC à ne recueillir gen 2, et ne comprennent pas gen 1 ou gen 0 ?

Si ce n'est pas possible, y a-t-il une raison pour que le GC soit conçu de cette façon?

+0

Pourquoi voudriez-vous faire cela, c'est-à-dire PAS collecter de gen 0 ou 1? Le GC .NET fonctionne mieux lorsqu'il est laissé à ses propres appareils. – thecoop

+0

J'en suis conscient. Fondamentalement, vous ne voulez JAMAIS forcer manuellement un GC, car ce sont des opérations intensives. Puisque c'est le cas, quand je vois la nécessité d'exécuter un GC, je voudrais qu'il fonctionne uniquement contre la génération spécifique, plutôt que de faire un GC complet. J'essaie d'être plus précis sur mon utilisation du GC, et il ne me laisse pas faire. – DevinB

Répondre

12

Ce n'est pas possible. Le GC est conçu de telle sorte qu'une génération 2 collection toujours recueille également la génération 0 et 1.

Modifier: Vous avez trouvé une source pour cela sur a GC developer's blog:

Gen2 GC exige une collection complète (Gen0 , Gen1, Gen2 et LOH! Large objets sont GC'ed à chaque GC Gen2 même lorsque le GC n'a pas été déclenché par manque d'espace dans LOH Notez qu'il n'est pas un GC qui ne recueille que de grands objets .) qui prend beaucoup plus de temps que collections de jeunes générations.

Edit 2: De même blog utilisant GC Efficacement Part 1 et Part 2 apparemment collections Gen0 et Gen1 sont rapides par rapport à une collection Gen2, afin qu'il me semble raisonnable que non seulement faire Gen2 être beaucoup de bénéfice de performance. Il pourrait y avoir une raison plus fondamentale, mais je ne suis pas sûr. Peut-être que la réponse est dans un article sur ce blog.

+0

Merci! S'il vous plaît noter, j'ai édité ma question pour demander aussi pourquoi il est contraint de cette manière. – DevinB

6

Puisque toutes les nouvelles allocations (autres que pour les objets volumineux) vont toujours aller dans Gen0, le GC est conçu pour toujours collecter à partir de la génération spécifiée et ci-dessous. Lorsque vous appelez GC.Collect(2), vous demandez au GC de collecter à partir de Gen0, Gen1 et Gen2. Si vous êtes certain que vous avez affaire à beaucoup d'objets volumineux (les objets qui, au moment de l'allocation, sont assez grands pour être placés sur le LOH), la meilleure option est de les mettre à null (Nothing in VB) quand vous en avez fini avec eux. L'allocation LOH tente d'être intelligente et de réutiliser les blocs. Par exemple, si vous avez alloué un objet de 1 Mo sur le LOH, puis que vous l'avez éliminé et que vous l'avez défini sur null, il vous restera un "trou" de 1 Mo. La prochaine fois que vous attribuez quelque chose sur le LOH qui est 1MB ou plus petit, il remplira ce trou (et continue de le remplir jusqu'à ce que la prochaine allocation est trop grande pour tenir dans l'espace restant, à quel point il sera allouer un nouveau bloc.)

Gardez à l'esprit que les générations dans .NET ne sont pas des choses physiques, mais sont des séparations logiques pour aider à augmenter les performances du GC. Puisque toutes les nouvelles allocations vont dans Gen0, c'est toujours la première génération à être collectée. Chaque cycle de collecte qui s'exécute, tout ce qui dans une génération inférieure survit à la collecte est «promu» à la prochaine génération la plus élevée (jusqu'à atteindre Gen2).

Dans la plupart des cas, le GC n'a pas besoin d'aller au-delà de la collecte de Gen0.L'implémentation actuelle du GC est capable de collecter Gen0 et Gen1 en même temps, mais il ne peut pas collecter Gen2 tant que Gen0 ou Gen1 sont en cours de collecte. (.NET 4.0 détend beaucoup cette contrainte et pour la plupart, le GC peut collecter Gen2 tandis que Gen0 ou Gen1 sont également collectés.)

+0

Votre explication est un excellent aperçu de la façon dont le GC fonctionne, mais il ne clarifie pas pourquoi il existe une contrainte qui empêche une collection * strictement * gen 1 ou gen 2. – DevinB

+2

La définition de 'myVar = null' n'a aucun effet. Voir le bas de http://www.bryancook.net/2008/05/net-garbage-collection-behavior-for.html – DevinB

+0

Je crois que la raison est que la promotion des objets aux générations supérieures est seulement évaluée (et effectuée) pendant GC, donc il n'y a peut-être rien (nouveau) à faire si vous ne collectez pas les générations inférieures ... –

0

Pour répondre à la question «pourquoi»: physiquement, il n'y a pas telle chose que Gen0 et Gen1 ou Gen2. Ils utilisent tous le ou les mêmes blocs de mémoire dans l'espace d'adressage virtuel. La distinction entre eux n'est vraiment faite que virtuellement en se déplaçant autour d'une limite imaginaire.

Chaque objet (petit) est alloué depuis la zone de tas Gen0. Si, après une collection, il survit, il est déplacé "vers le bas" dans cette zone du bloc de tas géré, qui a finalement été libéré des ordures. Ceci est fait en compactant le tas. Une fois la collection complète terminée, la nouvelle "bordure" pour Gen1 est définie sur l'espace juste après ces objets survécu. Donc, si vous vouliez simplement effacer Gen0 et/ou Gen1, vous ouvririez des trous dans le tas qui doivent être fermés en compactant les objets "pleins" de Gen0. Évidemment, cela ne ferait aucune différence puisque la plupart de ces objets seraient de toute façon des ordures. Il est inutile de les déplacer. Et inutile de créer et de laisser de grands trous sur le tas (autrement compactage).

+0

Conceptuellement, c'est le plus simple penser à Gen2 comme étant au bas du tas, avec gen1 immédiatement au-dessus, et gen0 au sommet. Les objets plus anciens sont toujours en dessous des objets plus jeunes. Lorsque le compactage se produit, le système parcourt tous les objets vivants de la ou des générations à compacter, en commençant par les objets les plus anciens, et déplace chaque objet vers le point disponible le plus bas. Le marqueur "top of gen2" est placé juste au-dessus de l'objet gen1 nouvellement copié le plus haut (maintenant dans gen2). De même, le marqueur "top of gen1" est placé juste au-dessus de l'objet gen0 nouvellement copié. – supercat

+0

Le but de tout ceci est de libérer l'espace contigu disponible au-dessus de gen0. Une grande partie de la raison pour compacter gen2 est de permettre aux objets gen1 de descendre; de même, compacter gen1 permet aux objets gen0 de descendre. Il y a quelques trucs bizarres .net utilise pour accélérer les déterminations de savoir si les objets sont "vivants"; en pratique, ils signifient que si un objet gen2 contient une référence à un objet gen0, l'objet gen0 ne sera pas collectionnable tant que la référence n'est pas détruite ou que l'objet gen2 lui-même peut être collecté. – supercat

0

Chaque fois que le système effectue une collecte d'ordures d'une génération particulière, il doit examiner chaque objet pouvant contenir une référence à un objet de cette génération. Dans de nombreux cas, les anciens objets ne contiendront que des références à d'autres objets anciens; Si le système fait une collection Gen0, il peut ignorer tous les objets qui ne contiennent que des références à celles de Gen1 et/ou Gen2. De même, s'il fait une collection Gen1, il peut ignorer tous les objets qui ne contiennent que des références à Gen2. Comme l'examen et le marquage des objets représentent une grande partie du temps requis pour la collecte des déchets, le fait de pouvoir ignorer entièrement les objets plus anciens représente un gain de temps considérable. Incidemment, si vous vous demandez comment le système "sait" si un objet peut contenir des références à des objets plus récents, le système a un code spécial pour définir quelques bits dans le descripteur de chaque objet si l'objet est écrit. Le premier bit est réinitialisé à chaque garbage collection, et s'il est toujours réinitialisé à la prochaine garbage collection, le système saura qu'il ne peut contenir aucune référence aux objets Gen0 (puisque tous les objets qui existaient quand l'objet a été écrit pour la dernière fois effacé par la collection précédente sera Gen1 ou Gen2). Le deuxième bit est réinitialisé à chaque garbage collection Gen1 et s'il est toujours réinitialisé lors de la prochaine garbage collection Gen1, le système saura qu'il ne peut contenir aucune référence aux objets Gen0 ou Gen1 (tous les objets auxquels il tient des références sont maintenant Gen2) . Notez que le système ne sait pas ou ne se soucie pas si les informations écrites sur un objet comprenaient une référence Gen0 ou Gen1. Le piège requis lors de l'écriture sur un objet non étiqueté est coûteux et entraverait grandement les performances s'il devait être traité à chaque fois qu'un objet est écrit. Pour éviter cela, les objets sont marqués chaque fois qu'une écriture se produit, de sorte que toute écriture supplémentaire avant la prochaine collecte de place peut se poursuivre sans interruption.

Questions connexes