Garbage Collector

Garbage Collector

Le Garbage Collector (GC) est implémenté depuis les débuts de la JVM. Son rôle consiste en fait à gérer en grande partie la mémoire à votre place. C’est pourquoi en Java il n’y a pas besoin de faire des delete de pointeurs comme en C par exemple. Cela dit le GC ne fait pas tout non plus, par exemple si vous conservez une liste en mémoire, que vous remplissez perpétuellement sans jamais la vider, au bout d’un moment vous obtiendrez quand même une fuite mémoire.

Le fonctionnement du GC

En Java, ce qu’on voit comme objets sont en fait des références vers ces objets. Chacun de ces objets contient de manière sous-jacente un compteur de références, qui sert à déterminer si un objet peut être supprimé ou non de la mémoire. Le fonctionnement est assez simple : à chaque fois qu’une nouvelle référence à l’objet apparaît, le compteur est incrémenté. Inversement, quand une référence vers un objet est supprimée, le compteur est décrémenté. Quand celui-ci est à zéro, cela signifie qu’au prochain passage du GC cet objet sera supprimé de la mémoire.

Lorsque trop d’objets ont été alloués sans être libérés, la JVM arrive à court de mémoire et invoque le GC pour en libérer. Celui-ci va alors faire une passe pour supprimer les objets dont le compteur de référence est à zéro. Des théories ont toutefois montré qu’une grosse majorité des objets d’une application ne vivait pas longtemps, autrement dit leur compteur de références tombait très vite à zéro. Pour des raisons de performance, la mémoire vue par le GC a donc été divisée en deux zones : le YoungGen, qui stocke les objets récents, et l’OldGen, qui stocke les objets à durée de vie longue. Il existe par ailleurs le PermGen sur lequel nous passons car il va disparaître avec Java 8. Un tel système permet de purger fréquemment les objets jeunes, ce qui est une opération peu coûteuse, et de ne purger que nettement plus rarement les vieux objets, car c’est très coûteux.

L’«âge» d’un objet est déterminé en fonction du nombre de cycles de GC auxquels il a « survécu ». De la même manière que chacun des objets comporte un compteur de référence, il possède également un compteur de cycles de GC. Ce dernier est incrémenté à chaque fois qu’il survit à un GC, et au bout d’un certain nombre de passages l’objet est transféré du YoungGen vers le OldGen.

Comme nous l’avons vu, le GC divise la mémoire entre la YoungGen et l’OldGen. Lorsque la mémoire YoungGen est pleine une Garbage Collection (GC) survient, et les actions suivantes sont effectuées :

  1. Supprimer les objets de la YoungGen dont le compteur de références est tombé à zéro.
  2. Incrémenter le compteur de GC des objets survivants.
  3. Déplacer les objets dont le compteur de GC a dépassé un certain seuil vers l’OldGen.

Cette opération est très rapide, de l’ordre de quelques millisecondes, et comme le YoungGen est assez petit elle s’exécute assez fréquemment.

Lorsque c’est l’OldGen qui est plein, une Full Garbage Collection (Full GC) survient. Celle-ci consiste à faire le travail d’un GC normal, mais également à supprimer de la zone OldGen tous les objets dont le compteur de références a atteint zéro. Il s’agit d’une opération coûteuse, pouvant durer plusieurs dizaines de secondes dans le pire des cas. Pendant ce temps votre application est ralentie, ou peut être complètement figée, suivant l’algorithme de GC choisi. Nous ne détaillerons pas ce dernier point dans l’article.

Et en quoi ça m’impacte, moi, développeur ?

Tout d’abord, un développeur peut déclencher un GC en appelant System.gc(). La spécification de Java indique que cette méthode envoie un message au GC indiquant qu’il serait bon qu’il se déclenche. Mais dans l’implémentation de référence de la JVM cette méthode déclenche dans les faits un full GC. Il vaut donc mieux éviter de l’utiliser, et dans ma vie de développeur ça ne m’est arrivé qu’une seule fois pour contourner un bug d’une librairie dont je n’avais pas le code.

L’autre implication est qu’il convient de ne pas allouer plus d’objets que nécessaire dans vos programmes, par exemple en initialisant plusieurs objets avec la même valeur au sein d’une même méthode pour les jeter immédiatement après. En effet à terme ceci déclenche de nombreux GC, et accélère le « vieillissement » de vos objets en les déplaçant prématurément dans la OldGen. Essayez donc plutôt de recycler vos objets. Pour une Collection, il vaut généralement mieux appeler la méthode clear() plutôt que d’initialiser une nouvelle collection du même type mais vide.


Laurelenne Poussin

0
0

Laisser un commentaire