Partager via


Gestion automatique de la mémoire

La gestion automatique de la mémoire est l’un des services fournis par Common Language Runtime pendant l’exécution managée. Le garbage collector du Common Language Runtime gère l’allocation et la libération de mémoire d’une application. Pour les développeurs, cela signifie que vous n’avez pas besoin d’écrire du code pour effectuer des tâches de gestion de la mémoire lorsque vous développez des applications managées. La gestion automatique de la mémoire peut éliminer les problèmes courants, tels que l’oubli de libérer un objet et provoquer une fuite de mémoire ou tenter d’accéder à la mémoire 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 la mémoire

Lorsque vous initialisez un nouveau processus, le runtime réserve une région contiguë d’espace d’adressage 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 ramasse-miettes alloue de la mémoire pour celui-ci dans l'espace d'adressage immédiatement après 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, il est presque aussi rapide que d’allouer de la mémoire à partir de la pile. En outre, étant donné que les nouveaux objets alloués consécutivement sont stockés contiguëment dans le tas managé, une application peut accéder aux objets très rapidement.

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 un regroupement, il libère la mémoire des objets qui ne sont plus utilisés par l’application. Il détermine quels objets ne sont plus utilisés en examinant les racines de l’application. Chaque application a un ensemble de racines. Chaque racine fait référence à un objet sur le tas managé ou a la valeur Null. Les racines d'une application incluent des champs statiques, des variables locales, des paramètres sur la pile d'un thread, ainsi que des registres de processeur. Le Garbage collector a accès à la liste des racines actives entretenues par le compilateur juste-à-temps (JIT) et le runtime. À l’aide de cette liste, elle examine les racines d’une application et, dans le processus, crée un graphique qui contient tous les objets accessibles à partir des racines.

Les objets qui ne se trouvent pas 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. Comme il découvre chaque objet inaccessible, il utilise une fonction de copie de mémoire pour compacter les objets accessibles en mémoire, libérant les blocs d’espaces d’adressage alloués aux objets inaccessibles. Une fois la mémoire des objets accessibles compactée, le ramasse-miettes apporte les corrections nécessaires aux pointeurs pour que les racines de l’application pointent vers les objets dans leurs nouveaux emplacements. Il positionne également le pointeur du tas managé après le dernier objet accessible. Notez que la mémoire est compactée uniquement si une collection découvre 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, l'environnement d'exécution alloue de la mémoire pour les objets volumineux dans un tas distinct. Le ramasse-miettes libère automatiquement la mémoire pour les grands objets. Toutefois, pour éviter de déplacer des objets volumineux en mémoire, cette 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 : 0, 1 et 2. L'algorithme de garbage collection du runtime est basé sur plusieurs généralisations que l'industrie informatique a observées en expérimentant des modèles de garbage collection. Tout d’abord, il est plus rapide de compacter la mémoire pour une partie du tas managé que pour l’ensemble du tas managé. Deuxièmement, les objets plus récents auront des durées de vie plus courtes et les objets plus anciens auront des durées de vie plus longues. Enfin, les objets plus récents ont tendance à être liés les uns aux autres et accessibles par l’application en même temps.

Le garbage collector du runtime stocke les nouveaux objets dans la génération 0. Les objets créés au début de la durée de vie de l’application qui survivent aux collections sont promus et stockés dans les générations 1 et 2. Le processus de promotion d’objet est décrit plus loin dans cette rubrique. Étant donné qu’il est plus rapide de compacter une partie du tas managé que l’ensemble du tas, 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 pour l’ensemble du tas managé chaque fois qu’il effectue une collection.

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 objet lorsque la génération 0 est pleine, le garbage collector découvre qu’il n’y a pas d’espace d’adressage restant dans la génération 0 à allouer pour 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é. Il s’agit de l’approche la plus efficace, car les nouveaux objets ont tendance à avoir des durées de vie courtes et on s’attend à ce que la plupart des objets de la génération 0 ne soient plus utilisés par l’application lorsqu’une collection est effectuée. En outre, une collection de génération 0 seule récupère souvent suffisamment de mémoire pour permettre à l’application de continuer à créer de nouveaux objets.

Après avoir effectué une collection de génération 0, le Garbage collector compacte la mémoire pour les objets accessibles comme expliqué dans la section Libération de la mémoire, plus haut dans cette rubrique. Le garbage collector promeut ensuite ces objets et considère que cette partie du tas managé appartient à la génération 1. Étant donné que les objets qui survivent aux collections ont tendance à avoir des durées de vie plus longues, il est judicieux de les promouvoir vers une génération plus élevée. Par conséquent, le ramasse-miettes n’a pas besoin de réexaminer les objets des générations 1 et 2 à chaque fois qu’il effectue une collecte de la génération 0.

Après avoir exécuté sa première opération garbage collection de la génération 0 et promu les objets accessibles à la génération 1, le garbage collector considère que le reste du tas managé appartient à la génération 0. Il continue d’allouer de la mémoire pour les nouveaux objets de la génération 0 jusqu’à ce que la génération 0 soit pleine et qu’il est nécessaire d’effectuer une autre 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 collection de génération 0 ne récupère pas suffisamment de mémoire pour que l’application termine correctement sa tentative de création d’un objet, le garbage collector peut effectuer une collection de génération 1, puis la génération 2. Si cela ne libère pas assez de mémoire, le garbage collector peut exécuter une collecte de génération 2, 1 et 0. Après chaque collecte, le garbage collector condense 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 collections sont promus vers la génération 2. Étant donné que le collecteur de déchets ne prend en charge que trois générations, les objets de la génération 2 qui survivent à une collecte restent en génération 2 jusqu'à ce qu'ils soient considérés comme inaccessibles lors d'une collecte future.

Libération de la mémoire pour les ressources non managées

Pour la majorité des objets créés par votre application, vous pouvez vous appuyer sur le garbage collector pour effectuer automatiquement les tâches de gestion de la mémoire nécessaires. Toutefois, les ressources non managées nécessitent un nettoyage explicite. Le type de ressource non managée le plus courant est un objet qui encapsule une ressource de système d’exploitation, telle qu’un handle de fichier, un handle de fenêtre ou une connexion réseau. Bien que le garbage collector puisse suivre la durée de vie d’un objet managé qui encapsule une ressource non managée, il n’a pas de connaissances spécifiques sur la façon de nettoyer la ressource. Lorsque vous créez 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 autorisez les utilisateurs de votre objet à libérer explicitement sa mémoire lorsqu’ils sont terminés avec l’objet. Lorsque vous utilisez un objet qui encapsule une ressource non managée, vous devez être conscient de Dispose et l’appeler si nécessaire. Pour plus d’informations sur le nettoyage des ressources non managées et un exemple de modèle de conception pour l’implémentation de Dispose, consultez Garbage Collection.

Voir aussi