Mémoire et garbage collector en Kotlin : comment éviter les fuites RAM et préserver les performances
\n\nVous êtes-vous déjà demandé où disparaît la mémoire vive de votre application Kotlin ? Le code est pourtant parfait, les coroutines volent, mais après une heure d'exécution, le programme ralentit ou plante avec une OutOfMemoryError. Un scénario familier ? Bienvenue dans le club de ceux qui veulent comprendre comment Kotlin gère la mémoire et pourquoi le garbage collector n'est pas un ennemi, mais votre meilleur allié (si vous savez comment l'apprivoiser).
Dans cet article, nous plongeons dans le sujet de la gestion de la mémoire en Kotlin et analysons le fonctionnement du garbage collector en Kotlin sur différentes plateformes. Vous apprendrez ce que sont la stack et le heap, comment éviter les fuites mémoire, et pourquoi WeakReference peut sauver votre projet. C'est parti !
Qu'est-ce que la mémoire en Kotlin ? Stack, Heap et leurs alliés
\n\nKotlin est un langage multiplateforme. Il se compile en bytecode JVM, en code natif (via Kotlin/Native) ou en JavaScript (Kotlin/JS). Sur chaque plateforme, la mémoire est organisée différemment, mais les bases restent les mêmes.
\n\nRappelons les fondamentaux. Lorsque votre programme démarre, le système d'exploitation lui alloue une zone mémoire. À l'intérieur de cette zone, on trouve deux « régions » principales :
\n\n- \n
- La Stack (pile) — elle stocke les variables locales, les primitives et les références aux objets. Elle fonctionne comme une pile d'assiettes : rapide, prévisible, mais de taille limitée. Un dépassement de pile provoque une
StackOverflowError. \n - Le Heap (tas) — le dépotoir de tous les objets créés via
newou un constructeur (val list = mutableListOf()). Le heap est volumineux mais plus lent, et c'est là que « vit » le garbage collector Kotlin. \n
Sur JVM, la stack et le heap fonctionnent de manière classique. Dans Kotlin/Native, la stack est la pile native du thread, tandis que le heap est géré par son propre garbage collector. Dans Kotlin/JS, tout repose sur le moteur JavaScript (V8, SpiderMonkey).
\n\nPoint clé : les références aux objets sont stockées dans la stack, tandis que les objets eux-mêmes se trouvent dans le heap. Tant qu'un objet possède au moins une référence « vivante » (depuis la stack ou un autre objet), le garbage collector ne le touche pas.
\n\nLe garbage collector en Kotlin : comment il fonctionne sur JVM
\n\nLorsque vous codez en Kotlin pour JVM (Android, serveur), vous bénéficiez de la puissance du Garbage Collector (GC) de la Java Virtual Machine. Ce n'est pas spécifique à Kotlin, mais il est essentiel d'en comprendre le mécanisme.
\n\nLa JVM moderne utilise G1GC (Garbage First) ou ZGC (dans les versions récentes). Voici comment cela fonctionne de manière simplifiée :
\n\n- \n
- Mark (Marquage) — le GC « parcourt » tous les objets vivants à partir des points racines (stack, champs statiques). Tout ce qu'il atteint est considéré comme vivant. \n
- Sweep (Nettoyage) — tout ce qui n'est pas marqué est considéré comme déchet et supprimé. \n
- Compact (Compaction) — les objets vivants sont déplacés pour éviter la fragmentation. \n
Avec G1GC, le heap est divisé en régions. Le GC collecte les régions contenant le plus de déchets (d'où son nom). Cela réduit les pauses, mais ne les élimine pas complètement.
\n\nEn Kotlin, vous pouvez influencer le garbage collector via les flags JVM (pour les serveurs) ou via GarbageCollectorMXBean (pour le monitoring). Mais le plus souvent, il suffit d'écrire un code qui ne gêne pas le travail du GC.
Exemple : comment le GC voit votre code
\n\nfun processData() {\n val list = mutableListOf<String>()\n for (i in 1..100_000) {\n list.add("Element $i")\n }\n // Ici, list est vivant tant que la méthode n'est pas terminée\n println(list.size)\n // Après la sortie de la méthode, list devient un déchet\n}\n\n\nUne fois processData() terminée, la référence à list disparaît de la stack, et l'objet devient candidat à la collecte. Simple, non ?
Spécificités de Kotlin/Native : un garbage collector sans JVM
\n\nKo