Cet article est issu du blog JavaScript de Mozilla. L’article original a été écrit en anglais par Bill McCloskey.

Firefox 16 va être la première version à supporter un ramasse-miettes (en anglais Garbage Collector ou GC) incrémentiel. C’est une fonctionnalité majeure, ayant nécessité plus d’un an de réalisation, qui va rendre Firefox plus fluide et moins lent. Avec un GC incrémentiel, Firefox répond plus rapidement aux clics et aux touches pressées. Les animations et les jeux seront aussi plus fluides.

L’objectif principal d’un ramasse-miettes est de récupérer la mémoire inutilisée des programmes écrits en JavaScript. L’espace mémoire récupéré peut ainsi être réutilisé pour de nouveaux objets JavaScript. Un nettoyage est réalisé habituellement toutes les 5 secondes ou presque. Avant l’arrivée du ramasse-miettes incrémentiel, Firefox était incapable de faire quoi que ce soit pendant le nettoyage de la mémoire, il ne pouvait pas répondre aux clics, dessiner des animations, ni exécuter du code JavaScript. Le passage du ramasse-miettes actuel est en général assez rapide mais dans certain cas, il peut prendre plusieurs centaines de millisecondes. Ce ralentissement peut être perçu par l’utilisateur et donc donner une impression de saccade assez frustrante (Sur Mac, il provoque même le redoutable curseur arc-en-ciel tournant).

Un ramasse-miettes incrémentiel corrige ce problème en divisant le travail du GC en petites unités. Au lieux de nettoyer pendant 500 millisecondes, un nettoyage incrémentiel peut diviser la travail en 50 petites tâches prenant chacune 10ms à effectuer. Et entre les phases de nettoyage, Firefox est libre de répondre aux clics et de dessiner des animations.

Bill McCloskey a créé une page de démonstration qui illustre la différence entre un GC incrémentiel et l’autre non-incrémentiel. Si vous utilisez Firefox 16 (ou plus), vous pouvez l’essayer ici (Si vous n’avez pas Firefox 16, l’exemple fonctionnera toujours, mais pas aussi bien). L’exemple montre la performance du ramassage des miettes sur un graphique animé. Pour mettre en évidence la différence entre un nettoyage incrémentiel et un non-incrémentiel, prenons 2 captures d’écran de cette illustration. La première a été prise sans le GC incrémentiel. Et le deuxième qui figure plus bas a été prise avec le GC incrémentiel.

nonincremental.png

L’axe horizontal représente le temps écoulé ; le point rouge se déplace vers la droite au cours du temps. L’axe vertical, représenté avec une échelle logarithmique, représente le temps écoulé entre le rendu de 2 images sur cette page de démonstration. Cette valeur est inversement proportionnelle au taux de rafraichissement (en anglais Frame Per Seconds, soit FPS). Idéalement, on souhaite dessiner une animation à 60 FPS, ce qui signifie que le temps entre chaque rendu d’image devrait être de 1000ms / 60, soit 16,667ms. Cependant, si le navigateur a besoin de nettoyer la mémoire ou d’exécuter une autre tâche, alors la pause sera plus longue entre 2 rendus.

Les 2 grands pics dans le graphique précédent sont dus aux GC non-incrémentiels. Le nombre rouge qui apparaît montre le temps du plus grand pic, dans ce cas 260ms. Cela signifie que le navigateur s’est figé pendant un quart de seconde, ce qui est très visible. En général, un nettoyage de la mémoire ne prend pas autant de temps. Cet exemple utilise volontairement beaucoup de mémoire, ce qui rend le nettoyage plus lent, et permet de mettre en avant les avantages des GC incrémentiels.

Pour générer le graphique précédent, il faut désactiver le GC incrémentiel en visitant la page « about:config » et en donnant la valeur « false » à l’option « javascript.options.mem.gc_incremental » (n’oubliez pas de le remettre sur « true » une fois l’expérience finie). Lorsque le GC incrémentiel est activé, le graphique ressemble à ça :

incremental.png

Ce graphique montre aussi 2 phases de nettoyage. Cependant, l’interruption la plus longue est seulement de 67ms. Cette pause est tellement petite qu’il est peu probable qu’on s’en rende compte. Toutefois, le nettoyage est plus étendu dans le temps. Sur l’image précédente, l’interruption était de 260 ms sur 260 ms (car le GC bloque complètement le navigateur). Sur la dernière image, chaque nettoyage dure approximativement 520 ms. Cela est dû au nettoyage incrémentiel de la mémoire qui opère en plusieurs fois ; entre chaque étape du nettoyage, Firefox peut dessiner un nouveau rendu et répondre aux actions de l’utilisateur. Ainsi la durée totale du nettoyage est deux fois plus importante, mais cela dérangera moins les utilisateurs.

En ce moment, l’équipe JS travaille activement sur le GC incrémentiel car il existe encore des phases de nettoyage qui ne sont toujours pas divisibles en sous-parties. La plupart du temps, ces phases ne prennent pas trop de temps, mais certain utilisateurs avec énormément d’onglets ouverts peuvent toujours avoir des blocages inacceptables. Firefox 17 et 18 devraient réduire encore plus les temps de pause.

Si vous voulez aller plus loin, vous pouvez installer MemChaser, une extension Firefox qui montre les temps d’interruption du ramasse-miettes au moment où ils surviennent. À chaque nettoyage, la plus longue interruption est affichée dans la barre d’extension en bas de la fenêtre. Il est important de noter que toutes les interruptions de Firefox ne sont pas uniquement dues au ramassage de miettes. Avec MemChaser vous pouvez corréler ces temps de pause avec l’activité du nettoyage de la mémoire.

Si une interruption sans nettoyage se produit, alors cela vient de quelque chose d’autre. Le projet Snappy est un projet de plus grande envergure qui vise à réduire les blocages pour rendre Firefox plus réactif. Il inclut des outils permettant d’identifier l’origine de ces interruptions dans le navigateur. L’un des plus impressionnants est le SPS profiler. Vous pouvez analyser et comprendre le code qui a été exécuté lorsque vous rencontrez un ralentissement. Si vous l’utilisez, vous pouvez alors faire un rapport de bug avec tout le détail de l’analyse  issue de cette extension !