Garbage Collection (GC) : ce que c'est, algorithmes et collecte des déchets dans le développement mobile

Auteur : IT Sectr Publié le : 2026-03-29 Temps de lecture : 10 min

La gestion automatique de la mémoire par le ramasse-miettes est un mécanisme clé de la plateforme Android, basé sur la machine virtuelle ART. Selon Google Android Documentation, 2026, le ramasse-miettes libère le développeur de la gestion manuelle de la mémoire, en supprimant automatiquement les objets qui n'ont plus de références. Sans GC, chaque allocation d'objet nécessiterait un appel explicite à free ou delete, ce qui est physiquement impossible dans l'écosystème Java avec des millions d'objets par seconde.

Points clés

  • Garbage Collection — mécanisme automatique de libération de mémoire en supprimant les objets inutilisés en Java et Android
  • Algorithmes de base — Mark-and-Sweep, Copying Collection et Generational Collection déterminent l'efficacité de la collecte
  • ART et Dalvik — deux implémentations de la machine virtuelle Android, où ART (Android Runtime) a remplacé Dalvik à partir d'Android 5.0
  • Pauses GC — arrêts de l'exécution de l'application pendant la collecte — cause principale de jank et de problèmes de performance
  • Optimisation GC — réduction des allocations, utilisation de pools d'objets et choix correct des types de collecte réduisent la charge du collecteur

Qu'est-ce que Garbage Collection (GC) ?

Garbage Collection (GC) est un processus automatique de détection et de libération de la mémoire occupée par des objets qui ne sont plus utilisés par le programme. Dans le contexte du développement mobile, le GC est utilisé sur la plateforme Android via la machine virtuelle ART, ainsi que dans la Java Virtual Machine standard.

Contrairement aux langages à gestion manuelle de la mémoire (C, C++), où le programmeur doit appeler explicitement free ou delete, le GC prend entièrement en charge le suivi du cycle de vie des objets. Le développeur crée de nouveaux objets via l'opérateur new, tandis que le collecteur détermine quand un objet devient inaccessible — c'est-à-dire lorsqu'il ne reste plus aucune référence active sur lui.

Les principales métriques d'efficacité du GC sont le temps de pause (pause time) et le débit (throughput). La pause est la période pendant laquelle l'exécution de l'application est arrêtée pour effectuer la collecte. Dans un environnement mobile, les pauses supérieures à 8–16 millisecondes sont perceptibles sous forme d'images perdues (jank).

Selon Google I/O 2019, ART dans Android 10 a réduit les pauses typiques du GC à 2–4 ms, soit 70% de moins par rapport à Dalvik dans Android 4.4. Néanmoins, une gestion inappropriée de la mémoire — allocation fréquente d'objets dans les boucles, création d'instances temporaires inutiles — reste la cause principale des problèmes de performance.

Comment fonctionne le ramasse-miettes : algorithmes de base

Toutes les implémentations de GC en Java et Android sont basées sur plusieurs algorithmes fondamentaux qui sont combinés pour atteindre un équilibre entre le temps de pause et la complétude du nettoyage. Comprendre ces algorithmes est essentiel pour écrire du code compatible GC.

Mark-and-Sweep

Mark-and-Sweep est l'algorithme le plus simple, fonctionnant en deux étapes. Dans la phase Mark, le collecteur parcourt le graphe d'objets en commençant par les références racines (root set) — variables locales, champs statiques, piles de threads. Chaque objet accessible est marqué avec un indicateur de vivant. Dans la phase Sweep, le collecteur parcourt tout le tas et libère la mémoire des objets non marqués.

L'inconvénient est la fragmentation de la mémoire : après Sweep, les zones libres alternent avec les zones occupées, rendant difficile l'allocation de gros objets. Dans les scénarios mobiles, c'est critique car le tas est généralement petit (64–512 MB sur Android).

Copying Collection

Copying Collection divise le tas en deux semi-espaces (semi-spaces). Les objets actifs sont copiés de manière compacte d'un semi-espace à l'autre, sans espacement. Après la copie, l'ancien semi-espace est entièrement déclaré libre. L'algorithme élimine complètement la fragmentation, mais nécessite deux fois plus de mémoire.

Dans les environnements mobiles, Copying Collection est utilisé par les collecteurs générationnels pour le nettoyage rapide des jeunes objets, qui meurent statistiquement tôt (hypothèse générationnelle faible).

Generational Collection

Generational Collection divise le tas en générations : Young Generation (jeunes objets) et Old Generation (objets anciens ayant survécu à plusieurs collectes). La collecte de la jeune génération (Minor GC) est effectuée fréquemment et rapidement, car la plupart des objets meurent jeunes. La collecte de l'ancienne génération (Major GC ou Full GC) se produit moins souvent mais dure plus longtemps.

java
// Démonstration du GC générationnel : les jeunes objets meurent vite
void processItems(List<Item> items) {
    List<Result> results = new ArrayList<>();       // vit pendant toute la méthode
    for (Item item : items) {
        Result r = new Result(item.getValue());    // meurt instantanément
        if (r.isValid()) {
            process(r);                               // r devient un déchet
        }
    }
    saveResults(results);                             // results passe en Old Gen
}

Dans cet exemple, les objets Result sont créés dans une boucle et deviennent immédiatement des déchets — ce sont des candidats idéaux pour le Young GC. L'objet results vit plus longtemps et migre vers Old Generation. La séparation des générations permet au Minor GC de nettoyer les jeunes objets en millisecondes sans toucher à l'ancien tas.

Garbage Collection sous Android : ART et Dalvik

Android a évolué de Dalvik VM à ART (Android Runtime), et l'implémentation du GC est l'une des principales différences entre eux. Comprendre l'architecture du GC sous Android aide à écrire du code qui minimise les pauses sur les appareils réels.

CaractéristiqueDalvik (jusqu'à 4.4)ART (5.0+)
Type de GCMark-and-Sweep avec Concurrent MarkGenerational + Concurrent
Pause typique10–30 ms2–4 ms
CompactageNon (seulement la fragmentation augmente)Oui (en arrière-plan, sans arrêter l'app)
Compilation AOTJIT (Just-In-Time)AOT + JIT (hybride)

Dalvik GC

Dalvik utilisait une combinaison de Mark-and-Sweep avec une phase concurrente. Concurrent Mark permettait à l'application de continuer à fonctionner pendant le parcours du graphe d'objets, mais la phase Sweep nécessitait l'arrêt de tous les threads (Stop-The-World). Sur les appareils avec peu de RAM (512 MB — 1 Go), les pauses atteignaient 30 ms, provoquant des ralentissements notables de l'interface. De plus, Dalvik ne compactait pas le tas, donc après une utilisation prolongée, la fragmentation augmentait et l'allocation de gros objets (par exemple, Bitmap) pouvait lancer OutOfMemoryError même avec suffisamment de mémoire libre totale.

ART GC

ART (Android Runtime) a introduit un collecteur générationnel avec compactage concurrent. Le tas est divisé en trois régions : Young, Mature (analogue à Old Generation) et Large Object Space (pour les objets de plus de 12 Ko). La collecte de la région Young se fait en parallèle sans arrêter les threads dans la plupart des cas. Dans Android 10+, Concurrent Copying a été introduit — le compactage s'exécute dans un thread d'arrière-plan sans Stop-The-World.

Grâce à l'architecture d'ART, les pauses typiques du GC ont été réduites à 2–4 ms, et dans les scénarios avec prédominance de jeunes objets — à 0.5–1 ms. Cela a permis aux appareils Android de maintenir 60 FPS stables même lors d'opérations mémoire actives.

Types de ramasse-miettes en Java

Dans l'écosystème Java, il existe plusieurs implémentations de GC, chacune avec son propre profil de performance. Pour le développement Android, le choix est limité à ART, mais la connaissance du GC Java est utile lors de l'écriture de code serveur pour les applications mobiles et lors du développement avec Kotlin Multiplatform.

Serial GC

Serial GC est un collecteur mono-thread avec arrêt complet de l'application (Stop-The-World). Chaque opération Mark, Sweep et Compact est effectuée par un seul thread. Les performances sont faibles — non utilisé pour les serveurs mobiles. Convient uniquement aux petites applications avec un tas jusqu'à 100 Mo.

Parallel GC

Parallel GC (également connu sous le nom de Throughput Collector) utilise plusieurs threads pour toutes les phases de collecte. Il est orienté vers le débit maximal (throughput) — minimisant le temps passé en GC par rapport au temps d'exécution de l'application. Activé via le flag -XX:+UseParallelGC dans la JVM.

G1 GC

G1 (Garbage-First) GC est le collecteur par défaut dans Java 9+. Le tas est divisé en régions de 1–32 Mo. G1 prédit le temps de pause et s'efforce de rester dans une limite spécifiée (200 ms par défaut). Priorité : les régions avec la plus grande quantité de déchets sont nettoyées en premier (d'où le nom). G1 est efficace pour les serveurs avec de grands tas (4–64 Go) avec des pauses prévisibles.

java
// Activation de G1 GC avec une pause cible de 100 ms
// java -XX:+UseG1GC -XX:MaxGCPauseMillis=100 -jar app.jar

public class MemoryMonitor {
    private static final long THRESHOLD = 512 * 1024 * 1024; // 512 MB

    public void checkHeapUsage() {
        Runtime rt = Runtime.getRuntime();
        long used = rt.totalMemory() - rt.freeMemory();
        if (used > THRESHOLD) {
            System.out.println("Heap usage exceeded threshold: " + used);
            System.out.println("Consider reducing allocations");
        }
    }
}

La surveillance du tas via Runtime permet de détecter les fuites de mémoire à un stade précoce. Si used dépasse 80% du tas maximum en fonctionnement stable — c'est un signe de fuite possible ou de consommation excessive de mémoire par l'application.

Problèmes GC et optimisation de la mémoire dans les applications mobiles

Même l'ART GC moderne ne résout pas tous les problèmes — une utilisation inappropriée de la mémoire reste la cause principale de jank et d'ANR (Application Not Responding). Examinons les principaux scénarios et méthodes d'optimisation.

Pauses GC et Jank

Pauses GC — arrêts des threads de l'application pendant la collecte. À l'écran, cela se manifeste par des images perdues, lorsque le temps entre deux images dépasse 16.6 ms (60 FPS). Si le GC dure 30 ms, une seule image est dessinée au lieu de deux — l'utilisateur voit un bégaiement de l'interface.

Les principales causes de longues pauses : un grand nombre d'objets vivants dans Old Generation, la fragmentation du tas, des Full GC fréquents. Pour le diagnostic, on utilise Android Studio Profiler et systrace.

Réduction de la charge GC

La règle principale du code compatible GC est de minimiser le nombre d'objets alloués. Chaque nouvel objet nécessite non seulement une allocation mémoire mais aussi une collecte ultérieure. Même si le GC est rapide, 1000 allocations supplémentaires par seconde génèrent 1000 vérifications pour le collecteur.

  • Évitez de créer des objets dans les boucles — déplacez la création hors de la boucle, réutilisez les variables locales
  • Utilisez des pools d'objets — pour Bitmap, byte[] et autres structures lourdes, utilisez Object Pool ou RecyclerView.ViewHolder
  • Préférez les primitifs — int au lieu d'Integer, float au lieu de Float évitent l'autoboxing
  • Utilisez SparseArray — au lieu de HashMap<Integer, V>, le SDK Android offre SparseArray, LongSparseArray qui fonctionnent avec des primitifs
  • StringBuilder au lieu de concaténation — chaque addition de chaîne crée un nouvel objet String

Fuites de mémoire

Une fuite de mémoire se produit lorsqu'un objet reste accessible alors qu'il n'est plus nécessaire. Le GC ne peut pas supprimer un tel objet et la mémoire s'épuise progressivement. Causes typiques : écouteurs non désinscrits, références statiques à Activity, classes anonymes capturant le contexte externe et Cursor/InputStream non fermés.

java
// Fuite mémoire : une classe anonyme garde une référence sur Activity
public void startTask() {
    new Thread(new Runnable() {                    // contient implicitement this (Activity)
        @Override
        public void run() {
            // opération longue...
            System.out.println("Done");
        }
    }).start();
}

// Correction : classe statique imbriquée + WeakReference
private static class TaskRunnable implements Runnable {
    private WeakReference<Activity> activityRef;

    TaskRunnable(Activity activity) {
        this.activityRef = new WeakReference<>(activity);
    }

    @Override
    public void run() {
        Activity act = activityRef.get();
        if (act != null) {
            // travail sécurisé avec Activity
        }
    }
}

Dans cet exemple, le Runnable anonyme capture une référence implicite à l'Activity. Tant que le thread est vivant — l'Activity ne peut pas être collectée par le GC, même si l'utilisateur a déjà fermé l'écran. La correction avec WeakReference + classe statique brise cette chaîne et permet à l'Activity d'être libérée.

Foire aux questions

En quoi le GC sous Android diffère-t-il du GC en Java ?

Le GC sous Android (ART) est un collecteur générationnel avec compactage concurrent, optimisé pour les appareils mobiles à mémoire limitée. Java GC (G1, ZGC) sont des collecteurs côté serveur avec de grands tas et des pauses prévisibles. ART GC n'utilise pas de flags JVM — tout le réglage est effectué automatiquement au niveau de l'OS.

Qu'est-ce que Stop-The-World en GC ?

Stop-The-World est le moment où le collecteur met en pause tous les threads de l'application pour parcourir en toute sécurité le graphe d'objets ou libérer la mémoire. Plus le STW est long, plus le jank est perceptible. ART a réduit le temps STW typique à 2–4 ms grâce à son architecture générationnelle.

Comment détecter une fuite de mémoire sous Android ?

Utilisez Android Studio Memory Profiler — il montre la croissance du tas, le nombre d'allocations et permet de faire un Heap Dump. Pour une analyse approfondie, utilisez LeakCanary — la bibliothèque détecte automatiquement les fuites et montre la chaîne de références empêchant la collecte GC.

Quand se produit Full GC et pourquoi est-il dangereux ?

Full GC est une collecte complète de toutes les générations du tas, y compris Old Generation. Dans les applications mobiles, Full GC peut durer 50–200 ms, provoquant un jank ou ANR notable. Causes principales : fragmentation du tas, fuites de mémoire, dépassement du seuil d'Old Generation.

Comment Kotlin aide-t-il à éviter les fuites de mémoire ?

Kotlin fournit des coroutines avec concurrence structurée — l'annulation de la portée annule automatiquement toutes les coroutines enfants, empêchant les fuites. Kotlin a également le délégué lazy pour l'initialisation paresseuse et des fonctions de portée qui réduisent le nombre d'objets temporaires.

Résumé

  • Garbage Collection — gestion automatique de la mémoire en supprimant les objets inaccessibles, fondement d'Android Runtime
  • Mark-and-Sweep — algorithme de base avec collecte en deux phases, souffre de fragmentation du tas
  • Copying Collection — élimine la fragmentation en copiant les objets vivants dans un semi-espace compact
  • Generational GC — divise le tas en générations (Young/Old), accélérant la collecte des jeunes objets à courte durée de vie
  • ART dans Android — collecteur générationnel avec compactage concurrent et pauses de 2–4 ms, remplaçant Dalvik dans Android 5.0
  • Optimisation GC — réduction des allocations, pools d'objets, primitifs au lieu de wrappers et SparseArray au lieu de HashMap réduisent la charge du collecteur
  • Diagnostic — Android Studio Profiler, systrace et LeakCanary sont les principaux outils pour identifier les problèmes mémoire

Nous développerons une application mobile clé en main

IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.

Discuter du projet

Lisez aussi