August 7, 2025•6 min

Partie 1 : GC Pauses et Latence: Le coût caché des langages de haut niveau

m
mayo

Les langages de haut niveau comme Java, Python ou JavaScript gèrent la mémoire automatiquement. Mais cela vient avec des compromis.

Que se passe-t-il avec ce code ?

String message = "hello";

Cela crée un objet sur en mémoire (heap). Mais finalement, cette mémoire doit être récupérée. Et c'est là qu'intervient le Garbage Collector (GC).

Comment chaque Langage Gère la mémoire

Java: Collections Stop-the-World

Le runtime de Java a plusieurs mécanismes de garbage collection ou générationnel. La plupart ne sont pas bloquant. Mais à un moment donné il lui est nécessaire de s'exécuter en bloquant tous les autres thread ("Stop-the-world"). Et cela créé des blocages, ou latences non prévisibles. C'est pour cette raison que typiquement on n'utilisera jamais Java pour un système de freinage d'urgence mais plutôt un language système (C/C++ ou Rust) car n'a pas d'indisponibilité imprévisible. La trace "Full GC" suivante met en évidence cet évennement.

[GC (Allocation Failure) 8192K->1024K(10240K), 0.0057 secs]
[Full GC (Ergonomics) 8192K->512K(19456K), 0.0234 secs]

Le GC de Java s'exécute en background, mettant ton application en pause de manière imprévisible. Même les GC modernes comme G1 peuvent faire des pauses de millisecondes.

Python: Reference Counting + Cycles

import gc
gc.collect()  # Collection manuelle
# Retourne: nombre d'objets collectés

Python compte les références aux objets, mais a besoin d'un collector séparé pour les références circulaires. Les deux ajoutent de l'overhead à chaque opération.

JavaScript: Generational Collection

// Aucun contrôle direct - V8 décide quand collecter
global.gc(); // Disponible seulement avec --expose-gc flag

V8 gère la mémoire automatiquement sans contrôle développeur. Les pauses arrivent quand le moteur décide.

Qui décide du moment où la mémoire disparaît ? Java stop-the-world les threads gèlent pendant le balayage coût : pics de latence Python refcount + cycles chaque affectation incrémente un compteur coût : overhead constant JavaScript générationnel, V8 le moteur choisit le moment, pas toi coût : aucun contrôle Rust aucun collecteur libéré à l'accolade fermante, à chaque fois coût : nul à l'exécution un runtime décide — tu le découvres après le compilateur décide — tu peux le lire Les trois stratégies de GC échangent débit ou prévisibilité contre du confort ; Rust déplace la décision à la compilation

L'Impact réel

Concrétement, voici quelques expériences mettant en évidence ce problème.

Une indexation Elasticsearch qui prend des dizaines d'heures en entreprise

Exécution initiale:  200GB corpus → 2 heures
Après pression mémoire: Mêmes données → 12 heures

Cause: GC a passé 70% du temps à nettoyer

Pics de latence Service Web : le CPU est sans arrêt occupé par le GC et introduit des latences dans les réponses aux appelles d'API

Réponse normale: 50ms
Pendant pause GC: 2000ms (40x plus lent!)
Requête normale 50ms — terminé Requête pendant une pause GC Pause GC — thread gelé, durée imprévisible Même requête, même code — 2000ms au total, 40x plus lent, et tu ne contrôles pas quand cela arrive

Comparaison GC

Langage Type GC Ton Contrôle Prévisibilité
Java Generational Flags JVM Faible
Python Reference + Cycle Module gc Très Faible
JavaScript Generational Aucun Très Faible

Les coûts Cachés

Overhead Mémoire:

  • Java: 2-8 bytes par header d'objet
  • Python: 28+ bytes minimum par objet
  • JavaScript: Metadata V8 variable

Overhead CPU:

  • 5-30% du temps CPU passé dans le GC
  • Reference counting à chaque assignment (Python)
  • Write barriers pour le generational GC

Pics de Latence:

  • Temps de pause imprévisibles
  • Pire sous pression mémoire
  • Impossible de garantir les temps de réponse

Quand le GC Devient un problème

Les systèmes de trading

Exigence: <1ms temps de réponse Réalité: N'importe quelle pause GC tue les performances

Les systèmes temps réel (automobile, automatismes..etc)

Exigence: Budget constant de 16ms (60fps)
Réalité: Frame drops pendant la collection

Traitement de données à Grande Échelle

Exigence: Traiter des TBs efficacement
Réalité: L'overhead GC grandit avec la taille du dataset

Le coût, en résumé

✅ Le GC facilite le développement
❌ La latence est imprévisible
❌ Les performances se dégradent sous charge
❌ Aucun contrôle sur quand les pauses arrivent
❌ Overhead mémoire et CPU toujours présent


La Question: Quelles sont les alternatives si on ne veut pas de Garbabe collection ?

Voir mon autre post: "Gestion Manuelle de la Mémoire: Pourquoi C/C++ N'est Pas la Réponse"

Retour au blog
Partager ::