mars
2005
Ce qui suit est la traduction d’un article en anglais sur le fonctionnement du «garbage collector » avec le Compact Framework. Cet article ne décrit pas à 100% le fonctionnement du GC sur le CF mais réponds simplement à quelques questions fréquemment posées. Dès que j’aurai le temps, d’autres traductions viendront…
Note : si vous trouvez des fautes, merci de me le signaler
Model de Collecte
Commençons par jeter un œil sur le model de collecte utilisé par le Compact Framework. Dans cette section, je vais décrire les conditions qui déclenche une collecte et les étapes spécifiques exécutées lorsque le «collecteur » fonctionne.
Une collecte est initiée lorsque :
– 750 KB d’objets sont alloués
– une application est déplacée en «background »
– une erreur d’allocation mémoire survient
– une application appelle la méthode «GC.Collect »
Lorsqu’une collecte se produit, le compact CLR exécute les étapes suivantes :
1. Placement de tous les threads dans un état sûr «safe point ». Avant qu’une collecte puisse se produire, le CLR doit placer les threads dans un état connu en respectant le tas (heap) du Garabage collector. Du coup, aucun threads n’est en état d’altérer la collecte du tas (heap) lorsque celle ci démarre. Pour faire ceci, le CLR place tous les threads dans un état tel qu’il n’y a pas de code managé en cours d’exécution. Dans cet état sûr, les threads ne sont pas autorisés à continuer leur travail tant que la collecte n’est pas terminée (excepté pour le thread courant qui exécute la collecte).
2. Marquer tous les objets qui descendent de « live roots ». Une fois que tout les threads sont dans un état sûr, les objets du tas (heap) peuvent être examinés. Ceux qui descendent du « live roots » comme les variables locales du la portée courante, ainsi que les variables « static » sont marqués. Noté qu’il n’y a pas de thread spécial qui existe pour effectuer le GC. Le thread qui tourne quand le GC est déclenché est le thread qui effectue la collecte.
3. Effectuer la libération de tous les objets qui ne sont pas marqués et placer ceux-ci dans la queue de « finalization ». La mémoire de tous les objets non marqués est libérée. Les objets qui disposent d’un « finalizer » sont placés dans la queue de « finalization ».
4. Occasionnellement, compactage du « GC heap ». Si le CLR détermine que le « GC Heap » est trop fragmenté, le « heap » est compacté.
5. Lancement du « Jitted code » dans certain scénarios. Le compact Framework CLR maintient le « Jitted code » dans une mémoire cache interne. Au lieu de recompilé une méthode à chaque fois qu’elle est appelée, le code natif pour cette méthode est simplement retrouvé dans le cache. Lorsque qu’une erreur d’allocation mémoire survient, ou lorsqu’une application est déplacée en « background », le contenu de cette mémoire cache est libéré pour avoir plus de mémoire disponible pour le système.
6. Lancement du « finalizer » pour tous les objets de la queue de « finalization ». Apres la libération des objets qui ne sont plus utilisés, et un éventuel compactage du tas ou/et suppression de la mémoire cache (voir paragraphe précédent), la collecte est considérée comme terminée et les threads sont autorisés à reprendre leur travail. En « background » le CLR utilise un thread dédié pour lancer le « finalizers » de chaque objet de la queue de « finalization ». Ces objets seront libérés à la prochaine collecte.
Différences entre le .Net CF collector et le collector du Framework complet ?
Le compact Framework cible différents scénarios que le Framework complet. La ou le compact Framework est spécifique pour les périphériques aux ressources limitées, le Framework complet supporte différents scénarios comme par exemple une application client/serveur avec SQL Server et ASP.NET hébergée sur une machine multiprocesseur. Il est donc normal de trouver des différences entre le GC du compact Framework et le GC du Framework complet. Les différences principales sont :
– un model de collecte différent. Comme nous l’avons vu, le compact Framework a un model standard de marquage et de collection de champ avec occasionnellement un compactage du heap. La méthode de libération des objets qui ne sont plus utilisés est la même, par contre le model de collecte sur le Framework complet est différent. En particulier, le Framework complet utilise un model de génération dans lequel les objets qui survivent à la collecte sont promus dans une génération supérieure (voir developpez.com) qui elle est moins souvent collecté. Le garbage collector sur le .NET Framework s’arrange pour que l’analyse des objets qui ne sont pas susceptible d’être collecté prenne le moins de temps possible. Il y a d’autres différences. Par exemple, les identifiants des objets exceptionnellement grands sont différents des objets normaux et les événements qui déclenchent la collecte sont différents. Le chapitre 19 du livre de Jeff Richter « Applied .Net Framework Programming » fournit des ressources sur le fonctionnement du Framework complet.
– Peu configurations du GC. Du fait que le Framework complet supporte une variété de scénarios applicatifs, le GC est autorisé a fonctionné dans différents modes spécifiques pour chacun des scénarios. En particulier la tactique de collecte utilisée par le Framework complet est différente suivant si vous utilisez une application interne sur une «Workstation » ou si vous utilisé une application sur un serveur multiprocesseur. Par exemple, sur une «Workstation » le mode de fonctionnement du GC est un mode concurrentiel utilisant les threads afin que l’interface graphique continue de répondre. Le compact Framework ne supporte pas ces modes. Ceci vient du fait que les scénarios d’applications sont réduits et la collecte elle même doit être réduite, un model simple est utilisé sur les périphérique ou l’application s’exécute.
– « Code pitching » (lancement de code). Le garbage collector du compact Framework libère le code JIT (en cache) afin de libérer la mémoire dans certains scénarios. Le garbage collector du .NET Framework ne dispose pas de cette capacité – la quantité de code JIT dans la mémoire est libre de se développer sans limites.
Quelles sont les ramifications d’un appel à GC.Collect () ?
Vous avez probablement entendu «oui il y a une API que vous pouvez utiliser pour forcer le GC, mais inutile de l’appelée, le GC peut faire le travail mieux que vous ». Voilà pourquoi vous entendez dire cela. Le lancement du GC et son exécution sont coûteux. Comme nous l’avons vu, les threads sont suspendus, il faut traverser le graphe d’objets et déplacer des blocks de mémoire. A chaque fois que vous forcez une collecte, vous forcez le compact CLR à mettre au minimum tout les threads dans un état sûr dans lequel la collecte peut s’exécuter, puis a scanner le graphe d’objet. En clair, des appels répétés à GC.Collect () peut avoir de très sévères conséquences sur les performances de votre application. Le GC du compact Framework se déclenche automatiquement quand il y a pour 750KB d’objets créés. Apres, la collecte se fait de manière occasionnelle – elles ne se produisent pas seulement quand de la mémoire est épuisée. De plus, lorsque l’appel à GC.Collect () revient, il n’est pas garanti que tous les « finalizer » d’objets ont fini de fonctionner. Donc vous ne pouvez pas forcer une collecte dans l’espoir de détruire un objet en particulier (utiliser le pattern Dispose).
Est il possible d’empêcher le GC de se lancer ?
Il n’y pas d’API que vous pouvez utiliser pour être empêcher le lancement du GC. Le temps que mets la collecte dépends du nombre d’objet que vous alloués. La seule chose qui permet de se prémunir d’une collecte est d’alloués le minimum d’objets. Souvenez vous qu’il y a des scénarios ou une allocation peut arriver sans que vous le sachiez. Le meilleur exemple est le « boxing ». Faire du « boxing » sur une valeur nécessite la création d’une référence dans le tas, sans pour autant que vous ayez fait appel à « new ». Nous pourrions être tentés de penser qu’un appel à GC.Collect () peut être employé pour faire de collectes régulièrement, mais comme décrit ci-dessus de tels appels sont susceptibles d’affecter négativement les performances de votre application.