Sujet 01 · Memory

It’s Raining Mem

Un petit jeu pixel art bricolé un dimanche. Il a un memory leak.

Les gouttes disparaissent bien du paysage, mais pas forcément du heap. Nous allons explorer un nouvel outil du panneau Memory : Allocations on timeline.

Le lab It’s Raining Mem animé : un paysage en pixel art sous la pluie, avec les commandes de pluie, le réglage de durée de vie des gouttes et les indicateurs de mesure.
Un terrain généré de façon procédurale et une gestion mémoire volontairement désastreuse : It’s Raining Mem!
Comme le disait notre slogan d’origine : Invest & Infest, Dercetech Does The Rest!

1. Intro

Un réglage simple, beaucoup de gouttes

L’interface est relativement simple : l’intensité de la pluie et un slider règlent la durée de vie d’une goutte après son arrivée au sol. Chaque goutte est gérée individuellement ; c’est idéal pour créer un code gourmand en ressources qui n’accomplit pas grand-chose pour autant. Chacun son hobby.

Le bas du lab de pluie : le slider Durée de vie d’une goutte au sol réglé à 15 secondes, avec les compteurs de gouttes, de mémoire JavaScript et de CPU.
Plus les gouttes restent au sol, plus le monde doit en suivre, en déplacer et en vérifier à chaque tick. Sur cet i7-13650HX, environ 2 000 gouttes occupent déjà 12 % d’un cœur. Waouh.

Avant de profiler, survolez 01-raining-mem/src/simulation/rain-world.js : le code est commenté et les méthodes ont des noms qui disent simplement ce qu’elles font.

On reconnaît l’influence d’Oh No More Lemmings, sur Atari ST, et de The Sandbox, sur iPhone 3GS. Le générateur procédural est basique : une variance maximale de ±2 pixels, quelques couleurs, et le tour est joué. C’est open source : servez-vous.

2. Créez le problème

Mode déluge

Passez en mode déluge. Demandez à Tony Montana — Scarface, oui, les références millenial — et le tour est joué :

Quelques étapes

  1. Ouvrez DevTools.
  2. Passez la pluie sur Rain ×4.
  3. Réglez la durée de vie des gouttes au sol au maximum.
Meme montrant une personne déguisée en Jésus avec le texte Flood It Again.
Donnez-lui quelques dizaines de secondes : le lab a désormais tout ce qu’il faut pour accumuler du travail et des gouttes.

Observez la mémoire

Les gouttes disparaissent, et pourtant la mémoire continue de monter. L’eau, elle, stagne à quelques pixels de haut.

Les types de profiling disponibles dans le panneau Memory de Chrome DevTools, avec Allocations on timeline sélectionné.
Dans Memory, choisissez Allocations on timeline avant de lancer l’enregistrement.
Le lab It’s Raining Mem ouvert avec Chrome DevTools : Allocations on timeline est en cours d’enregistrement et le bouton Enregistrer est entouré.
Rechargez la fenêtre, puis appuyez sur Enregistrer.

Explorez le résultat

L’approche est exploratoire. Filtrez sur un type de classe lorsque vous cherchez quelque chose de précis. Sinon, sélectionnez une période dans la timeline et regardez ce qui ne colle pas : plus de 2 000 Raindrop dans le heap alors qu’il n’en reste qu’une poignée au sol ? Code smell.

Animation du panneau Memory de Chrome DevTools : la timeline d’allocations est sélectionnée et le constructor Raindrop affiche plus de 2 000 instances encore en mémoire.
La sélection d’une période limite la vue aux objects alloués pendant cette période et encore vivants à la fin de l’enregistrement.

3. Solution

La reference de trop

Spoiler alert. Pas de physique quantique ici : chaque goutte expirée quitte bien le paysage, mais elle est ajoutée à rainfallSamples. Ce tableau appartient au monde entier ; tant qu’il garde la reference, le garbage collector ne peut pas libérer la goutte.

Le même geste se produit lorsque le bouton Remove raindrops vide l’écran. Disparaître du canvas ne suffit pas : une reference JavaScript quelque part suffit à maintenir un object dans le heap.

Le correctif

Au moment où une goutte expire, ne la rangez pas ailleurs. La ligne marquée ci-dessous est à retirer — ici comme dans clearDrops().

01-raining-mem/src/simulation/rain-world.js
else if (now - drop.settledAt >= this.dropLifetime) {
  drop.expiredAt = now;
  this.rainfallSamples.push(drop); // ← retirez cette reference
  continue;
}

Mesurez à nouveau

Rejouez exactement le même scénario : Rain ×4, durée maximale, puis enregistrement avec Allocations on timeline. Les anciennes Raindrop devraient pouvoir disparaître après leur expiration ; les barres bleues persistantes et leur compteur doivent alors nettement reculer.