Gestion automatique de la mémoire

Mise à jour : novembre 2007

La gestion automatique de la mémoire est un des services que le Common Language Runtime fournit au cours de l'exécution managée. Le « garbage collector » du Common Language Runtime gère l'allocation et la libération de mémoire pour une application. Les développeurs n'ont donc plus à écrire du code pour exécuter leurs tâches de gestion de mémoire lors du développement d'applications managées. La gestion automatique de la mémoire permet d'éliminer des problèmes fréquents tels que l'oubli de libération d'un objet ou les fuites de mémoire ou encore les tentatives d'accès à la mémoire à la recherche d'un objet qui a déjà été libéré. Cette section décrit la façon dont le « garbage collector » alloue et libère la mémoire.

Allocation de mémoire

Lorsque vous initialisez un nouveau processus, le runtime réserve une région d'espace d'adressage contigu pour le processus. Cet espace d'adressage est appelé le tas managé. Le tas managé garde un pointeur vers l'adresse qui sera allouée au nouvel objet du tas. À l'origine, ce pointeur indique l'adresse de base du tas managé. Tous les types référence sont alloués sur le tas managé. Lorsqu'une application crée le premier type référence, la mémoire est allouée pour le type à l'adresse de base du tas managé. Lorsque l'application crée l'objet suivant, le « garbage collector » lui alloue de la mémoire dans l'espace d'adressage qui suit immédiatement le premier objet. Aussi longtemps que de l'espace d'adressage est disponible, le « garbage collector » continue à allouer de l'espace pour de nouveaux objets selon la même procédure.

L'allocation de mémoire à partir du tas managé est plus rapide que l'allocation de mémoire non managée. Étant donné que le runtime alloue de la mémoire pour un objet en ajoutant une valeur à un pointeur, elle est pratiquement aussi rapide que l'allocation de mémoire à partir de la pile. En outre, puisque les nouveaux objets auxquels un espace mémoire est alloué sont stockés à la suite dans le tas managé, une application peut accéder très rapidement aux objets.

Libération de la mémoire

Le moteur d'optimisation du « garbage collector » détermine le meilleur moment pour lancer une opération garbage collection sur base des allocations de mémoire effectuées. Lorsque le « garbage collector » effectue une opération garbage collection, il libère la mémoire pour les objets qui ne sont plus utilisées par l'application. Il détermine les objets qui ne sont plus utilisés en examinant les racines de l'application. Chaque application possède un jeu de racines. Chaque racine fait référence à un objet du tas managé ou, à défaut, a la valeur null. Les racines de l'application comprennent des pointeurs d'objet globaux et statiques, des variables locales et des paramètres d'objet de référence sur la pile d'un thread et des inscriptions au Registre de l'UC. Le « garbage collector » a accès à la liste des racines actives que maintiennent le compilateur juste-à-temps et le runtime. En utilisant cette liste, il examine des racines d'application et crée, au cours du processus, un graphique qui contient tous les objets accessibles à partir des racines.

Les objets non compris dans le graphique ne sont pas accessibles à partir des racines de l'application. Le « garbage collector » examine les objets inaccessibles et libère la mémoire qui leur a été allouée. Au cours d'une opération garbage collection, le « garbage collector » examine le tas managé pour y détecter les blocs de mémoire occupés par des objets inaccessibles. Chaque fois qu'il détecte un objet inaccessible, il utilise une fonction de copie de mémoire pour compacter les objets accessibles en mémoire et libérer les blocs d'espaces d'adressage alloués aux objets inaccessibles. Lorsque la mémoire allouée aux objets accessibles a été réduite, le « garbage collector » procède aux corrections de pointeurs nécessaires pour que les racines des applications pointent vers les nouveaux emplacements des objets. Il positionne aussi le pointeur du tas managé après le dernier objet accessible. Vous remarquerez que la mémoire est compactée uniquement si une collection détecte un nombre significatif d'objets inaccessibles. Si tous les objets du tas managé survivent à une collection, il n'est pas nécessaire de procéder à un compactage de la mémoire.

Pour améliorer les performances, le runtime alloue de la mémoire pour les objets de grandes dimensions dans un tas séparé. Le « garbage collector » libère automatiquement la mémoire des objets de grande dimension. Cependant, pour éviter de déplacer de grands objets en mémoire, la mémoire n'est pas compactée.

Générations et performances

Pour optimiser les performances du Garbage Collector, le tas managé est divisé en trois générations numérotées 0, 1 et 2. L'algorithme d'opération garbage collection du runtime se base sur plusieurs généralisations que l'industrie des logiciels a observées, en réalité, en essayant divers schémas de garbage collection. En premier lieu, il est plus rapide de compacter la mémoire pour une partie du tas managé que pour le tas tout entier. Deuxièmement, les nouveaux objets ont des durées de vie plus courtes que les objets plus anciens. Enfin, les nouveaux objets sont fréquemment liés entre eux et l'application y accède à peu près au même moment.

Le « garbage collector » du runtime stocke de nouveaux objets dans la génération 0. Les objets créés à un stade précoce de la vie de l'application et qui survivent aux opérations garbage collection sont promus et stockés dans les générations 1 et 2. Le processus de promotion d'objets est décrit plus avant dans cette rubrique. Parce qu'il est plus rapide de compacter une partie du tas managé que le tas tout entier, ce schéma permet au « garbage collector » de libérer la mémoire dans une génération spécifique plutôt que de libérer la mémoire de l'intégralité du tas managé chaque fois qu'une opération garbage collection est effectuée.

En fait, le « garbage collector » procède à une opération collection lorsque la génération 0 est complète. Si une application tente de créer un nouvel objet lorsque la génération 0 est complète, le « garbage collector » observe qu'il n'y a plus d'espace d'adressage disponible dans la génération 0 à allouer à l'objet. Le « garbage collector » effectue une opération garbage collection en essayant de libérer de l'espace d'adressage pour l'objet dans la génération 0. Le « garbage collector » commence par examiner les objets de la génération 0 plutôt que tous les objets du tas managé. Cette approche est la plus efficace parce que les nouveaux objets ont en règle générale une durée de vie courte et qu'une grande part des objets de la génération 0 ne seront plus utilisés par l'application lorsqu'une opération garbage collection sera effectuée. En outre, une opération garbage collection de la génération 0 récupère souvent à elle seule suffisamment de mémoire pour permettre à l'application de continuer à créer de nouveaux objets.

Après que le « garbage collector » effectue une opération garbage collection de la génération 0, il compacte la mémoire pour les objets accessibles comme expliqué dans la section Libération de la mémoire, plus haut dans cette rubrique. Ensuite, le « garbage collector » promeut ces objets et examine la partie de la génération 1 du tas managé. Étant donné que les objets qui survivent aux opérations garbage collection ont en règle générale une durée de vie plus longue, il est logique de les promouvoir à une génération supérieure. En conséquence, le « garbage collector »n'a pas à réexaminer les objets des générations 1 et 2 chaque fois qu'il effectue une opération garbage collection de la génération 0.

Lorsque le « garbage collector » a effectué sa première opération garbage collection de la génération 0, et promeut les objets accessibles à la génération 1, il examine le reste de la génération 0 du tas managé. Il continue à allouer de la mémoire aux nouveaux objets de la génération 0 jusqu'à ce que celle-ci soit remplie et qu'il soit nécessaire de procéder à une nouvelle opération garbage collection. À ce stade, le moteur d'optimisation du « garbage collector » détermine s'il est nécessaire d'examiner les objets des générations plus anciennes. Par exemple, si une opération garbage collection de la génération 0 ne récupère pas assez de mémoire pour que l'application achève sa tentative de création d'un nouvel objet, le « garbage collector » peut effectuer une opération garbage collection de la génération 1, puis de la génération 0. Si la mémoire ainsi récupérée n'est pas encore suffisante, le « garbage collector » pourra effectuer une opération garbage collection des générations 2, 1 et 0. À l'issue de chaque opération garbage collection, le « garbage collector » compacte les objets accessibles dans la génération 0 et les promeut à la génération 1. Les objets de la génération 1 qui survivent aux opérations garbage collection sont promus à la génération 2. Vu que le « garbage collector » ne prend en charge que trois générations, les objets de la génération 2 qui survivent à une opération garbage collection restent dans la génération 2 jusqu'à ce qu'ils soient détectés comme inaccessibles dans une future opération garbage collection.

Libération de mémoire pour des ressources non managées

Pour la majorité des objets créés par votre application, vous pouvez laisser au « garbage collector » le soin de réaliser automatiquement les tâches de gestion de mémoire requises. Cependant, les ressources non managées requièrent un nettoyage explicite. Le type le plus répandu de ressource non managée est un objet qui enveloppe une ressource de système d'exploitation telle qu'un handle de fichier ou de fenêtre ou une connexion réseau. Bien que le « garbage collector » soit en mesure de suivre la durée de vie d'un objet managé qui encapsule une ressource non managée, il ne possède pas de connaissances spécifiques sur la façon de nettoyer une ressource. Lors de la création d'un objet qui encapsule une ressource non managée, il est recommandé de fournir le code nécessaire pour nettoyer la ressource non managée dans une méthode Dispose publique. En fournissant une méthode Dispose, vous donnez la possibilité aux utilisateurs de votre objet d'en libérer explicitement la mémoire lorsqu'ils ont fini de s'en servir. Lorsque vous utilisez un objet qui encapsule une ressource non managée, vous devez garder à l'esprit la méthode Dispose et l'appeler si nécessaire. Pour plus d'informations sur le nettoyage de ressources non managées et un exemple de modèle de design pour l'implémentation de la méthode Dispose, consultez Garbage collection.

Voir aussi

Tâches

Garbage Collection, exemple de technologie

Concepts

Processus d'exécution managée

Référence

GC

Autres ressources

Garbage collection