00 - Pourquoi la mémoire compte meme avec un garbage collector
Ce que tu vas apprendre
- Pourquoi "JavaScript gere la mémoire pour moi" est un mythe dangereux
- Ce qu'est une fuite mémoire en pratique (et ce que ca coûte)
- Ce que cette serie de 21 articles couvre
Prerequisites
Aucun. C'est le point de depart de la serie.
Le serveur qui redemarrait toutes les 4 heures
En 2023, je bosse sur un projet Node.js en production. Un service qui traite des webhooks Stripe. Le truc tourne, les clients paient, tout va bien. Sauf que le monitoring montre des redemarrages réguliers. Toutes les 4 heures environ, le process crash avec un FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory.
On avait mis un --max-old-space-size=512 en pensant que ca suffirait. Le service ne faisait "que" recevoir des webhooks et mettre à jour une base PostgreSQL. Rien de fou. Mais la mémoire montait, montait, montait. Lineairement. Sans jamais redescendre.
Le coupable ? Un Map global qui stockait les webhooks en cours de traitement pour éviter les doublons. On ajoutait des entrees. On ne les supprimait jamais. En 4 heures, ce Map contenait 200 000 entrees et pesait plus de 400 Mo.
Trois lignes de code pour le fix. Un setTimeout qui supprimait les entrees apres 5 minutes. Mais trois semaines avant qu'on trouve le problème.
Le mythe du "JavaScript gere la mémoire pour moi"
C'est la phrase qu'on entend le plus souvent quand on parle de mémoire en JS. Et c'est vrai... en partie. Le garbage collector (GC) libéré automatiquement la mémoire des objets que tu n'utilises plus. Tu n'as pas a faire de malloc ou de free comme en C.
Mais le GC ne peut libérer que ce qui est devenu inaccessible. Si tu gardes une référencé vers un objet, meme par accident, meme via une closure oubliee, meme via un listener que tu n'as pas retire, le GC considéré que tu en as encore besoin. Il ne libéré rien.
Tu penses que ca se passe comme ca :
[Objet cree] ---> [Objet utilise] ---> [GC le supprime]
En realite :
[Objet cree] ---> [Objet utilise] ---> [Reference oubliee quelque part]
|
v
[GC ne touche pas]
|
v
[Memoire qui monte]
Un onglet a 2 Go, ca existe
J'ai vu un collegue ouvrir une SPA React en dev. Il navigue entre les pages, fait des recherches, ouvre des modales. Au bout de 30 minutes, l'onglet consomme 2 Go de RAM. Chrome ralentit. Le ventilateur du MacBook se met en route.
Le problème : chaque navigation montait un composant qui enregistrait un addEventListener('resize', ...) sur window. Le composant se demontait, mais le listener restait. Chaque listener retenait le scope du composant, qui retenait le state, qui retenait les donnees API. 50 navigations = 50 copies de tout le state en mémoire.
C'est pas un cas extreme. C'est un pattern que je vois dans la moitie des projets React/Vue que j'audite.
Ce que cette serie couvre
Cette serie fait 21 articles. Voici la carte :
| # | Sujet |
|----|-------------------------------------|
| 00 | Introduction (tu es ici) |
| 01 | Stack vs Heap |
| 02 | Cycle de vie de la memoire |
| 03 | Le garbage collector |
| 04 | V8 en profondeur |
| 05 | Les 6 fuites classiques |
| 06 | Closures et memoire |
| 07 | WeakRef et WeakMap |
| 08 | FinalizationRegistry |
| 09 | Chrome DevTools (heap snapshot) |
| 10 | Profiling Node.js |
| 11 | Strings et memoire |
| 12 | Arrays et typed arrays |
| 13 | Object shapes et hidden classes |
| 14 | Pools et recyclage d'objets |
| 15 | Web Workers et memoire partagee |
| 16 | SharedArrayBuffer et Atomics |
| 17 | Streams et backpressure |
| 18 | WASM et memoire lineaire |
| 19 | Optimisation React/Vue |
| 20 | Checklist production |
Chaque article est independant, mais je recommande de les lire dans l'ordre si tu debutes sur le sujet. Les 4 premiers posent les bases theoriques. Les articles 05 a 08 couvrent les problèmes concrets. Le reste va dans le détail des outils et des cas avances.
A qui s'adresse cette serie
A toi si tu ecris du JavaScript ou du TypeScript au quotidien et que tu n'as jamais ouvert l'onglet "Memory" de Chrome DevTools. A toi si ton serveur Node.js a des pics de mémoire inexpliques. A toi si tu veux comprendre pourquoi ton appli React rame apres 10 minutes d'utilisation.
Je ne vais pas te noyer dans la theorie. Chaque article contient du code, des schemas, et des cas concrets. On va parler de V8 parce que c'est le moteur de Chrome et Node.js, mais les principes s'appliquent a tous les runtimes JS.
Sur paltemps.fr, j'ai du traquer une fuite mémoire liee a un cache d'images meteo qui ne se vidait jamais. Cette serie, c'est tout ce que j'aurais voulu savoir avant de passer trois jours a fixer dans le profiler.
Résumé
- Le garbage collector ne te protégé pas des fuites mémoire. Il protégé contre les objets devenus inaccessibles, pas contre les références oubliees.
- Les fuites mémoire en JS sont reelles, frequentes, et couteuses.
- Cette serie couvre tout : de la theorie (stack, heap, GC) aux outils (DevTools, profiling) en passant par les patterns concrets (closures, WeakMap, pools).
Suivant : Stack vs Heap - ou vivent tes variables