Publié le 24/11/2020 Par Cristiano Davino

Dans la première partie de notre dossier, nous nous sommes attachés à présenter le concept de Garbage Collector (GC) de Java dans ses grandes lignes afin de mieux comprendre quel était son périmètre d’utilisation. Dans cet article-ci, nous tenterons d’expliquer le fonctionnement du garbage collector en Java en particulier dans les différentes zones de mémoire mais aussi les différents types de GC.

I/ Fonctionnement du Garbage Collector dans les différentes zones de mémoire 

a- Dans la Young Generation (Minor GC) 

Les objets nouvellement créés commencent leur cycle de vie dans la zone Young Generation. Cette zone est ensuite divisée en un espace Eden, dans lequel les objets nouvellement créés sont alloués, et deux espaces Survivor (« S0 » ou « To Space » et « S1″ou « From Space »). 

État de la mémoire avant l’exécution du minor GC. Les objets sont créés à l’intérieur de la zone Eden et les espaces « Survivor » sont vides. 

Lorsque la JVM n’est pas en mesure de récupérer de l’espace dans la zone Eden, elle exécute le minor CG. Au cours de cette phase, les objets qui ne sont pas accessibles sont marqués comme « ordures ». La JVM sélectionne l’un des espaces des survivants comme « Vers l’espace » (S0 par exemple) et copie les objets accessibles dans cette zone, augmentant ainsi l’âge d’un objet réalisable de 1. 

État de la mémoire après l’exécution d’un Minor GC (Partie 1). En rouge, les objets identifiés comme « inaccessibles » dans la phase de « Mark ». 

S’il y a des objets présents dans l’espace Survivor « From Space », ils sont copiés dans le nouvel espace « To Space », ce qui augmente leur âge. Une fois terminée, la JVM échange alors les deux espaces survivants (s1 devient « To space » et S0 devient « From Space »). 

Les objets accessibles qui ne rentrent pas dans l’espace « Survivor » seront déplacés vers l’espace « Old Generation ». Du point de vue du fonctionnement du garbage collector, ce processus est appelé « promotion prématurée ». Tous les éléments accessibles sont des racines GC et ne seront pas supprimés pendant cette phase. Le GC supprime les objets inaccessibles et vide l’espace Eden. Le processus se répète pour chaque minor GC. Lorsque les objets atteignent le seuil d’âge maximal, ces objets sont copiés dans l’ancien espace.  

Il existe une option de niveau JVM appelée « MaxTenuringThreshold » pour spécifier le seuil d’âge de l’objet afin de promouvoir l’objet en espace permanent. Par défaut, la valeur est 15.  

Il est donc clair que le minor GC récupère la mémoire de l’espace « Young Generation ». Parfois, la pause de l’application est négligeable. Le minor GC sera effectué avec un seul thread ou multithread, basé sur le collecteur GC appliqué. 

b- Old Generation (Major GC) 

Si notre application nécessite de créer et/ou de détruire un nombre important d’objets, alors le processus « minor GC » sera lancé plusieurs fois et, à long terme, l’espace mémoire de l’ancienne génération sera plein. À ce moment précis, notre application n’a plus de mémoire disponible pour créer de nouveaux objets. C’est donc ici qu’un processus de « major GC » est lancé. 

Le GC majeur est effectué pour libérer de l’espace dans « l’espace permanent ». Étant donné que cet espace mémoire est plus grand, ce processus se produit moins fréquemment. Le collecteur garde une trace d’un nombre maximum de GC pour chaque objet et, lorsque ce seuil est dépassé, l’objet est nettoyé puis meurt. Si ce seuil n’est pas suffisant pour répondre aux exigences de promotion, un « Full GC » est activé. 

Quelles sont alors les possibilités que la JVM déclenche un GC majeur ? 

  • En appelant System.gc () ou Runtime.getRunTime ().Gc (), nous suggérons à la JVM d’exécuter le GC (on ne dit pas que le JVM le fait). 
  • Si la JVM décide qu’il n’y a pas assez d’espace dans l’espace occupé. 
  • Si pendant un minor GC, la JVM ne peut pas allouer suffisamment de mémoire à partir de l’Eden et / ou des Survivors. 
  • Lorsqu’il n’y a pas assez d’espace pour charger de nouvelles classes en utilisant l’option « MaxMetaspaceSize« . 

c- Tous les espaces mémoire (Full GC) 

Dans le cadre du fonctionnement du garbage collector, le full GC est une opération qui nettoie et compacte l’ensemble du tas et affecte toutes les zones de mémoire Young et Old (Perm et Metaspace si disponibles) dans le but de libérer la mémoire autant que possible. Cette phase concerne le nettoyage des métadonnées et les classes qui ne sont plus utilisées. 

Représentation de l’état de la mémoire pendant les phases de « Mark, Sweep et Compact » 

II/ Les différents types de Garbage Collector 

Le GC compte principalement quatre implémentations : 

  • Serial Garbage Collector 
  • Parallel Garbage Collector 
  • CMS Garbage Collector 
  • G1 Garbage Collector 

Les versions ultérieures de Java ont introduit d’autres GC : 

  • Epsilon Garbage Collector (v11) 
  • Z Garbage Collector (v11) 
  • Shenandoah Garbage Collector (v12) 

Pour chacune de ces implémentations, le fonctionnement du garbage collector est différent. Voici un petit descriptif de ces implémentations. 

a- Serial Garbage Collector 

Implémentation simple conçue pour un environnement à thread unique ou à faible mémoire. Gèle tous les threads d’application lors de son exécution et utilise un seul thread pour le nettoyage. Non recommandé dans les applications multithread car il arrête tous les threads d’application. 

b- Parallel Garbage Collector 

Il s’agit du GC par défaut de la JVM (appelé Throughput Collector). Contrairement au Serial GC, il utilise un thread multiple pour la gestion de l’espace, mais bloque également les autres threads de l’application lors de l’exécution du GC. Si nous utilisons ce GC, nous pouvons spécifier le nombre maximal de threads pour la collecte des objets et le temps de pause, le throughput et la taille du Heap. 

c- CMS Garbage Collector 

L’implémentation CMS (Concurrent Mark Sweep) utilise plusieurs threads simultanés pour analyser le Heap (« mark ») et pour chercher les objets inutilisés à nettoyer (« sweep »). Conçu pour les applications qui préfèrent des pauses plus courtes et peuvent se permettre de partager les ressources du processeur pour le nettoyage pendant que l’application est en cours d’exécution. Les applications qui utilisent ce type de GC réagissent en moyenne plus lentement, mais n’arrêtent pas de le faire pendant la phase de nettoyage. 

d- G1 Garbage Collector 

Ou G1 (Garbage First). Disponible à partir de JDK 7, il est conçu pour les applications qui s’exécutent sur des machines multiprocesseurs avec un grand espace mémoire. Une fois le Heap divisé en sections de tailles similaires, chacune avec une plage contiguë de mémoire virtuelle, il exécute la phase de « mark » simultanément et, dès que terminé, sait quelles sections sont pour la plupart vides. G1 se concentre d’abord sur ces zones, produisant généralement une quantité importante d’espace libre. Il remplace le CMS car il est plus efficace en termes de performances. 

e- Epsilon Garbage Collector 

Epsilon est un GC non opérationnel ou passif. Il alloue la mémoire pour l’application, mais ne collecte pas les objets inutilisés. Lorsque l’application épuise le Heap Java, la JVM s’éteint. Cela signifie que le Garbage Collector d’Epsilon permet aux applications de s’épuiser en mémoire et d’échouer. Le but de ce Garbage Collector est de mesurer et de gérer les performances des applications. 

f- Z Garbage Collector 

ZGC effectue simultanément tous les travaux coûteux, sans arrêter l’exécution des threads d’application pendant plus de 10 ms, ce qui le rend approprié pour les applications qui nécessitent une faible latence et / ou utilisent un très grand Heap. ZGC essaiera de définir le nombre de threads lui-même, et c’est généralement correct. Mais si ZGC a trop de threads, votre application en souffrira. S’il n’en a pas assez, vous créerez des objets inaccessibles plus rapidement que le GC ne peut les collecter. 

g- Shenandoah Garbage Collector 

Le fonctionnement du garbage collector Shenandoah est très spécifique. C’est un Garbage Collector dit ultra-low-pause qui réduit les temps de pause GC en effectuant plus de travail de collecte de objets inaccessibles en même temps que le programme Java en cours d’exécution. Le CMS et le G1 effectuent tous deux le marquage simultané des objets vivants. Shenandoah ajoute le compactage simultané. Il utilise des zones de mémoire pour gérer les objets qui ne sont plus utilisés, et ceux qui sont vivants et prêts à être comprimés. Shenandoah ajoute également un pointeur de redirection à chaque objet dans le Heap et l’utilise pour contrôler l’accès à l’objet. Shenandoah doit également collecter le Heap plus rapidement que l’application qu’il sert ne le permet. Si la pression d’allocation est trop élevée et qu’il n’y a pas assez de place pour de nouvelles allocations, il y aura un échec. Shenandoah offre les mêmes avantages que le ZGC avec de grands tas mais davantage d’options de réglage. 

Image des quatre types de Garbage Collectors 

III/ Finalize Method 

La méthode Finalize est appelée le finaliseur. Du point de vue du fonctionnement du garbage collector, elle est invoquée lorsque la JVM détermine que cette instance particulière doit être récupérée. Un tel finaliseur peut effectuer toutes les opérations, y compris ramener l’objet à la vie. En réalité, le moment ou le Garbage Collector appelle les finaliseurs dépend de l’implémentation de la JVM et des conditions du système. En d’autres termes, elles sont hors de notre contrôle. 

Dans les systèmes du monde réel, nous ne devrions jamais invoquer cela explicitement : 

  • Le premier problème notable associé aux finaliseurs est le manque de rapidité. Nous ne pouvons pas savoir quand un finaliseur est exécuté car la récupération de place peut se produire à tout moment. 
  • Étant donné que l’algorithme de récupération de place dépend de l’implémentation JVM, un programme peut très bien fonctionner sur un système tout en se comportant différemment au moment de l’exécution sur un autre. 
  • Coûteux : la JVM doit effectuer beaucoup plus d’opérations lors de la construction et de la destruction d’objets contenant un finaliseur non vide. 
  • Si un finaliseur lève une exception, le processus de finalisation est annulé et l’exception est ignorée, laissant l’objet dans un état corrompu sans aucune notification. 

IV/ Les évolutions du fonctionnement du Garbage Collector 

a- Pemanent Generation 

Le PermGen (génération permanente) est un espace spécial séparé de la mémoire principale qui conserve la trace des métadonnées de classe chargées dans cette espace. La JVM stocke tout le contenu statique dans cette section mémoire (méthodes statiques, variables primitives et références aux objets statiques).  

La taille de cette zone mémoire, par défaut, n’est pas très grande et il est possible de la modifier via les paramètres suivants : 

  • XX: PermSize = [taille] est la taille initiale ou minimale de l’espace 
  • XX: MaxPermSize = [taille] est la taille maximale 

Avec sa taille de mémoire limitée, la Permanent Generation est impliquée dans la génération du célèbre « OutOfMemoryError » (lorsque les « class loaders » ne sont pas nettoyés correctement, cela provoque une fuite de mémoire).  

G1 vous permet de nettoyer cet espace mémoire et effectue un GC complet, uniquement lorsque la Permanent Generation est pleine ou lorsque l’application crée des objets plus rapidement que la GC ne peut libérer. 

b- Metaspace 

Il remplace le PermGen à partir de Java 8. La différence la plus importante est la façon dont elle gère l’allocation de mémoire : les GC activent automatiquement le nettoyage des classes mortes une fois que l’utilisation des métadonnées de classe atteint la taille maximale de la Metaspace. Cette amélioration réduit le risque d’obtenir l’erreur « OutOfMemory« . 

Toutefois, il est possible d’apporter des améliorations en personnalisant le pourcentage de cet espace mémoire via les paramètres suivants : 

  • MetaspaceSize et MaxMetaspaceSize : définissez les limites supérieures de la Metaspace. 
  • MinMetaspaceFreeRatio : pourcentage minimum de capacité de métadonnées de classe libre après GC. 
  • MaxMetaspaceFreeRatio : pourcentage maximum de capacité de métadonnées de classe libre après une GC pour éviter une réduction de l’espace. 
Permanent Generation vs Metaspace 

Conclusion 

Le fonctionnement du garbage collector reste, aujourd’hui, un sujet d’étude et d’amélioration : de nouvelles GC ont été introduites avec les versions Java 11 et 12. Cependant, il n’existe pas de règle générale applicable à tous : chaque implémentation, avec sa configuration de la taille des espaces mémoire, est étroitement liée au type d’application et aux résultats de performance attendus. 

Links 

  • ;
  • ;
  • ;
  • ;
  • https://medium.com/@hasithalgamge/seven-types-of-Java-garbage-collectors-6297a1418e82 
  • https://www.infoq.com/articles/Java_Garbage_Collection_Distilled/ 
  • ;
  • https://medium.com/@kiranchowdhary/Java-memory-management-and-garbage-collection-e29d3c313d05 
  • https://medium.com/platform-engineer/understanding-Java-garbage-collection-54fc9230659a 
  • ;
  • ;
  • https://www.baeldung.com/Java-system-gc 
  • ;
  • https://examples.javacodegeeks.com/core-Java/Java-9-default-garbage-collector-tutorial/ 
  • https://blogs.oracle.com/poonam/about-g1-garbage-collector,-permanent-generation-and-metaspace 
  • ;
  • ;
  • ;
  • ;

Pas encore de commentaires

Publier un commentaire

Auteur

Cristiano Davino

Passionné par la technologie et les séries télévisées, j'ai commencé ma carrière à Rome, puis je suis allé à Milan et je me suis finalement installé en France.
Je travaille surtout avec technologie Java (backend), et j'ai un intérêt marqué pour le devops et le cloud.

Découvrir ses autres articles