Le garbage collector en .NET en 10 min

Appel au Garbage Collector

Un certain nombre de fonctions permettent d’exécuter le Garbage Collector.
GC.Collect() permet de forcer l’exécution du GC. Un paramètre permet d’indiquer la génération des objets pour lesquelles le GC sera exécutée. La méthode ne certifie pas de l’exécution immédiate du GC, elle permet juste d’indiquer au GC qu’il devra s’exécuter.
GC.GetGeneration(object) permet de connaître la génération d’un objet.
GC.WaitForPendingFinalizers() stoppe le thread en cours jusqu’à ce que toutes les fonctions “finalizers” ont terminés de vider leur file d’attente.
GC.KeepAlive(hr): permet d’indiquer au GC qu’un objet doit être maintenu vivant et ne doit pas être supprimé. La fonction doit être placée à la fin de la partie où on désire qu’il reste vivant:

ObjectType obj = new ObjectType(); 
utilisation de l'objet dans du code..... 
… autre code …. 
GC.KeepAlive(obj);

Cette fonction est utile si on fait appel à du code qui n’est pas lié à des objets managés, à des objets de la WinAPI ou des objets COM.

Object.Finalize()

On ne peut pas faire appel directement à cette fonction. Le GC appelle cette fonction pour libérer les ressources occupées par l’objet. On peut cependant guider l’exécution du GC:
GC.SuppressFinalize() empêchera au GC d’exécuter Finalize().
GC.ReRegisterForFinalize() pour rajouter l’objet dans la liste des objets pour lesquels le GC doit exécuter Finalize().

L’implémentation correspondant à Finalize() dans un objet est le destructeur.

Les objets ayant une implémentation de Finalize() prennent plus de temps pour être détruit. Quand un objet n’est plus accessible par d’autres objets managés, le GC vérifie s’il contient une implémentation de la fonction Finalize():
– s’il ne possède pas une implémentation de Finalize(), l’objet est marqué pour être collecté et sa mémoire est requisitionnée par le GC.
– s’il possède une implémentation de Finalize(), l’objet est marqué pour être finalisé mais il ne sera pas marqué pour être collecté. La méthode Finalize() sera exécutée à la fin de l’exécution du GC. Quand cette méthode sera exécutée, l’objet sera marqué pour être collecté. Au passage suivant du GC, l’objet sera effectivement détruit. Il faut donc 2 passages du GC.

http://msdn.microsoft.com/fr-fr/library/system.object.finalize%28v=vs.110%29.aspx

Génération des objets

Génération 0: objet à courte durée de vie. Le GC s’exécute souvent pour ces objets. Les nouveaux objets sont de génération 0 sauf les objets de grande tailles (qui sont de génération 2). Les objets de génération 0 pour lesquels le GC est appelé seront automatiquement détruits.
Génération 1: c’est un buffer entre les objets de génération 0 et les objets de génération 2 c’est-à-dire entre les objets à courte durée de vie et les objets à longue durée de vie.
Génération 2: ce sont les objets à longue durée de vie. Les objets statiques font parties de cette génération.

Les objets peuvent passer à une génération supérieure (promotion) si ils ont survécu au passage du GC. Les objets qui sont à la génération 2 restent dans cette génération si ils ont survécu au passage du GC. Plus il y aura des objets de génération 2 et plus il sera difficile pour les nouveaux objets d’être de la génération 2. Ainsi on empêche on évite que la génération 2 ne devienne trop grosse et que le GC prenne trop de temps pour s’exécuter.

Exécution du GC

Au demarrage du processus:
Le Garbage Collector alloue 16 ou 32 mo, il va décider de la taille de la génération 0. Ensuite, il donne la main à l’exécution du code.
L’allocation des objets peut se fait jusqu’à ce que la génération 0 soit rempli, ensuite le GC commence son exécution:
Phase de marquage où le GC marque les objets vivants
Réallocation des références des objets pour qu’ils soient compactés
Phase de compactage: le GC récupère l’espace où les objets morts se trouvaient et déplacent les objets qui ont survécus vers un emplacement à la fin du segment de mémoire allouée. Les objets de la génération 2 peuvent être déplacés vers des segments plus anciens. La plupart du temps, les objets de grandes tailles ne sont pas compactés car l’impact sur les performances seront trop importants. Il est possible de forcer le compactage des objets de grandes tailles en faisant appel à:

GCSettings.LargeObjectHeapCompactionMode

Au fur et à mesure, il va créer la génération 2 et déplacer les objets de la génération 1 à la génération 2 etc…

Remarque importante: le GC maîtrise bien la gestion des objets de génération 0 et 1 mais il maîtrise mal les objets qui sont dans la génération 2.

Le GC utilise les éléments suivants pour déterminer si les objets sont vivants ou non:
– Les éléments racines fournis par le JIT (Just-In-Time compiler): les paramètres des méthodes, les membres privés d’une classe, les registres du CPU.
– Les objets managés (GC handles): ces objets sont alloués par l’utilisateur ou par le CLR.
– Les données statiques: les objets statiques dans le domaine d’application pouvant référencés d’autres objets et peuvent permettre à d’autres threads d’être suspendus.

Pendant l’exécution du GC, les autres threads sont suspendus.

Points faibles du GC

– il met en pause l’exécution de tous les threads pour s’exécuter,
– un seul thread fait tout le travail,
– le marquage et le compactage de la génération 2 est long.

Manipulation de ressources non managées

La libération des objets non managées doivent être effectuée explicitement car le GC ne traite que les objets managés.

http://msdn.microsoft.com/en-us/library/ee787088%28v=vs.110%29.aspx

Weak references

Il est possible de créer une weak reference sur un objet de façon à permettre sa suppression dans certaines conditions. Ainsi, pour des objets de grandes tailles qui ne sont pas utilisés réguliérement, il est possible de rajouter une weak reference. Cette reference permettra d’avoir un lien assez faible pour retrouver la valeur de l’objet en cas de besoin et pas trop fort pour qu’en cas de besoin de mémoire le GC puisse le supprimer et réquisitionner la mémoire qu’il occupe.
La condition pour utiliser une weak reference est d’utiliser un objet de grande taille et qui n’est pas utilisé tout le temps.

Il existe 2 types de weak references:
Short: la cible de la référence est nulle si il a été réquisitionné par le GC. C’est la configuration par défaut.
Long: une longue weak reference est retenue lorsque la méthode Finalize de l’objet a été exécutée. L’objet pourra être recréé mais son état n’est pas prévisible. En fait, une fois que la méthode Finalize a été exécutée, l’objet peut être supprimé à tout moment en cas de besoin.


http://msdn.microsoft.com/en-us/library/ms404247%28v=vs.110%29.aspx

http://msdn.microsoft.com/en-us/library/system.weakreference%28v=vs.110%29.aspx

Notifications du GC

On peut recevoir des notifications du GC pour savoir quand il sera exécuté et quand il a terminé son exécution. Ces notifications peuvent être utiles pour des applications manipulant des données de grande taille et où les exécutions du GC peuvent poser problème.

Les fonctions à utiliser sont:
GC.RegisterForFullGCNotification(): pour indiquer que l’on souhaite être notifié. Si on indique un “maxGenerationThreshold” élevé, le seuil de notification sera élevé donc l’exécution du GC se fera plus tardivement et on recevra la notification plus tôt. Si le seuil est trop élevé, il faudra attendre longtemps avant qu’une nouvelle exécution se fasse.
Si le seuil est bas, la probabilité sera élevé que l’exécution du GC se fasse tôt et que la notification soit tardive.
GC.WaitForFullGCApproach(): permet d’indiquer que l’exécution potentiellement bloquante du GC est imminente.
GC.WaitForFullGCComplete(): permet d’indiquer que l’exécution bloquante du GC est terminée.
GC.CancelFullGCNotification(): permet d’indiquer que l’on ne souhaite plus être notifié pour les exécutions du GC.

Toutes ces méthodes doivent être utilisées pour que les notifications se fassent correctement. Il faut de plus vérifier le statut des notifications remontées par “WaitForGCApproach” et “WaitForGCComplete”.

http://msdn.microsoft.com/fr-fr/library/cc713687%28v=vs.110%29.aspx
http://www.abhisheksur.com/2010/08/garbage-collection-notifications-in-net.html

Mode d’exécution du GC

Configuration du GC sur un serveur

L’exécution du GC se fait sur plusieurs threads. Chaque thread s’exécute de façon non-concurrente.

Configuration du GC sur un poste de travail

Il existe qu’un seul thread pour le GC. Plusieurs configurations:
Concurrent: configuration par défaut. Le GC est exécuté toujours sur un thread à part qui marque constamment les objets à supprimer quand l’application est en cours d’exécution. Le GC fait le choix ou non de compacter les données en mémoire en fonction des performances. Le compactage induit la suspension des autres threads mais cette suspension est invisible sur l’interface. Cette configuration permet de meilleurs performances pour les applications avec une GUI.
Non concurrent: le thread du GC est inactif jusqu’à son exécution. Quand l’exécution commence, le GC marque tous les objets, libère la mémoire et la compacte. Les autres threads sont suspendus pendant l’opération. L’application peut ne pas réagir pendant un instant lors de l’exécution du GC.

Il est possible de modifier la configuration dans le fichier de configuration de l’application: le paramètre est gcConcurrent.

Leave a Reply