Mémoire et performance JS/TS - 00 - Pourquoi la mémoire compte meme avec un garbage collector

JavaScript gere la mémoire pour toi, mais ca ne veut pas dire que tu peux l'ignorer. Introduction a la serie sur la mémoire et la performance.

  1. 01 Mémoire et performance JS/TS - 00 - Pourquoi la mémoire compte meme avec un garbage collector
  2. 02 Mémoire et performance JS/TS - 01 - Stack vs Heap
  3. 03 Mémoire et performance JS/TS - 02 - Le cycle de vie de la mémoire
  4. 04 Mémoire et performance JS/TS - 03 - Le garbage collector
  5. 05 Mémoire et performance JS/TS - 04 - V8 en profondeur
  6. 06 Mémoire et performance JS/TS - 05 - Les 6 fuites mémoire classiques
  7. 07 Mémoire et performance JS/TS - 06 - Closures et mémoire
  8. 08 Mémoire et performance JS/TS - 07 - WeakRef, WeakMap et WeakSet
  9. 09 Mémoire et performance JS/TS - 08 - FinalizationRegistry : savoir quand le GC passe
  10. 10 Mémoire et performance JS/TS - 09 - DevTools Memory : investiguer dans Chrome
  11. 11 Mémoire et performance JS/TS - 10 - Profiling mémoire en Node.js
  12. 12 Mémoire et performance JS/TS - 11 - Détecter et corriger les fuites mémoire
  13. 13 Mémoire et performance JS/TS - 12 - ArrayBuffer et TypedArrays
  14. 14 Mémoire et performance JS/TS - 13 - Workers et mémoire partagee
  15. 15 Mémoire et performance JS/TS - 14 - Streams et backpressure
  16. 16 Mémoire et performance JS/TS - 15 - Fuites mémoire en React
  17. 17 Mémoire et performance JS/TS - 16 - Serveurs Node.js et mémoire
  18. 18 Mémoire et performance JS/TS - 17 - Mémoire et Docker
  19. 19 Mémoire et performance JS/TS - 18 - Optimisations mémoire
  20. 20 Mémoire et performance JS/TS - 19 - Comparaison avec d'autres langages
  21. 21 Mémoire et performance JS/TS - 20 - Tester la mémoire
  22. 22 Mémoire et performance JS/TS - 21 - Glossaire

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

Sources

Réservez un audit gratuit de 30 minutes. Je vous montre concrètement ce qu'on peut automatiser.