LeakCanary est une bibliothèque open source de Square pour la détection automatique des fuites mémoire dans les applications Android. Elle s'intègre dans le processus de développement et surveille en temps réel le cycle de vie des Activity, Fragment, ViewModel et autres composants, signalant les fuites dès qu'elles se produisent. Selon Square Open Source, la bibliothèque est utilisée dans des milliers de projets et est considérée comme le standard de facto pour le diagnostic mémoire sous Android.
Points clés
LeakCanary est une bibliothèque pour la détection automatique des fuites mémoire dans les applications Android, développée par Square. Elle s'intègre dans le processus de compilation de l'application et surveille automatiquement si les objets qui devraient être détruits (Activity, Fragment, View) restent en mémoire. Lors de la détection d'une fuite, LeakCanary génère un heap dump et analyse la chaîne de références qui retient l'objet.
La bibliothèque est devenue un standard dans la communauté Android : selon GitHub, le projet compte plus de 28 000 étoiles et est utilisé dans les applications de Google, Uber, Airbnb et Facebook. LeakCanary est disponible en deux versions principales : la version classique 1.x (avec configuration manuelle) et la version moderne 2.x (intégration automatique via ContentProvider). La version 2.x ne nécessite pas de modifier la classe Application — la dépendance suffit pour un fonctionnement complet.
La tâche principale de LeakCanary est de détecter quand un objet continue d'exister en mémoire après la fin de son cycle de vie. C'est typique des fuites via des champs statiques, des singletons, des callbacks non désenregistrés, des classes anonymes et des closures qui capturent des objets externes.
Les fuites mémoire sous Android sont plus critiques que sur le bureau en raison de la RAM limitée des appareils mobiles. Même une fuite de 5–10 Mo à chaque transition d'écran peut provoquer une OutOfMemoryError après 30 à 40 minutes d'utilisation de l'application. LeakCanary détecte ces problèmes dès la phase de développement, sans attendre un crash en production.
LeakCanary utilise des références faibles (WeakReference) combinées à un garbage collection forcé. Lorsqu'une Activity ou un Fragment appelle onDestroy, LeakCanary crée une WeakReference sur cet objet et déclenche le GC après un court délai (5 secondes par défaut). Si l'objet est toujours accessible via la WeakReference après le GC, il est retenu par une référence forte — une fuite est enregistrée.
Après avoir détecté une fuite, LeakCanary effectue un heap dump (vidage mémoire) — un instantané complet de la mémoire de l'application au format HPROF. Ensuite, l'analyseur intégré (Shark pour la version 2.x) construit un graphe d'accessibilité depuis les GC Roots jusqu'à l'objet qui fuit et trouve le chemin le plus court — la chaîne de références qui maintient l'objet en mémoire.
// Logique simplifiée de détection de LeakCanary
class ObjectWatcher {
private val watchedReferences = CopyOnWriteArrayList<KeyedWeakReference>()
fun watch(watchedObject: Any, description: String) {
val reference = KeyedWeakReference(watchedObject, description)
watchedReferences.add(reference)
BackgroundHandler.postDelayed({
checkForLeaks()
}, 5000)
}
private fun checkForLeaks() {
GcTrigger.runGc() // GC forcé
for (ref in watchedReferences) {
if (ref.get() != null) {
onLeakFound(ref) // l'objet a survécu au GC — c'est une fuite
}
}
}
}
Le point clé est l'appel forcé à GcTrigger.runGc(). Sans lui, il est impossible de distinguer un objet qui a vraiment fui d'un objet que le GC n'a pas encore collecté. LeakCanary le fait jusqu'à trois fois : si après trois cycles de GC l'objet est toujours en mémoire, la fuite est confirmée.
Shark est l'analyseur de heap dump intégré à LeakCanary 2.x, écrit en Kotlin. Contrairement à l'ancien analyseur HAHA, Shark ne charge pas tout le fichier HPROF en mémoire, mais parcourt son graphe d'objets avec des allocations minimales. Cela réduit la consommation de RAM pendant l'analyse de 50 Mo à 2–5 Mo et raccourcit le temps d'analyse de 30 secondes à 1–3 secondes.
L'installation de LeakCanary 2.x dans un projet Android moderne prend une seule ligne dans build.gradle. La bibliothèque utilise ContentProvider pour l'initialisation automatique — pas besoin de modifier la classe Application ni d'ajouter du code à MainActivity. La dépendance est ajoutée uniquement pour les builds debug, afin que les APK de release ne contiennent pas de code supplémentaire.
// build.gradle (app/module)
dependencies {
// debugImplementation — bibliothèque uniquement pour les builds debug
debugImplementation "com.squareup.leakcanary:leakcanary-android:2.14"
}
Après avoir ajouté la dépendance et reconstruit le projet, LeakCanary apparaît automatiquement dans l'application. Au premier lancement, la bibliothèque affiche une notification système confirmant l'activation. Toutes les fuites détectées apparaissent sous forme de notifications — appuyer sur une notification ouvre un écran avec un rapport détaillé (LeakTrace).
Pour la personnalisation, vous pouvez créer votre propre AppWatcherInstaller et remplacer les paramètres : délai d'attente GC, liste des types d'objets suivis, activation de la sauvegarde du heap dump sur le disque. Cependant, pour 90 % des projets, la configuration par défaut est optimale.
À partir de la version 2.12, LeakCanary prend en charge le suivi automatique de ViewModel, des portées de coroutines et des objets State de Compose. Aucune dépendance supplémentaire n'est nécessaire — la bibliothèque détecte automatiquement quels composants Jetpack sont utilisés dans le projet et active les détecteurs correspondants.
Un rapport LeakCanary (LeakTrace) est une chaîne de références à plusieurs lignes allant de la GC Root à l'objet qui fuit. Chaque ligne montre la classe et le champ par lesquels passe une référence forte. Le développeur doit lire la chaîne de bas en haut : la ligne du bas est l'objet qui fuit, la ligne du haut est le point d'entrée (GC Root).
Un LeakTrace typique ressemble à ceci : GC Root → champ statique d'Application → singleton → callback → Activity. Si un développeur voit cette chaîne, le problème est clair : le singleton contient un callback qui a capturé une référence à l'Activity. La solution est de remplacer la référence forte par une référence faible dans le singleton.
┬
├─ android.app.Application
│ Leaking: NO (Application — singleton)
│ ↓ Application.leakedActivities
├─ java.util.ArrayList
│ Leaking: NO (ArrayList — normal)
│ ↓ ArrayList[0]
├─ com.example.MainActivity
│ Leaking: YES (Activity destroyed but still in memory)
│ ↓ MainActivity.mCallback
├─ com.example.CallbackWrapper
│ Leaking: UNKNOWN
│ ↓ CallbackWrapper.mListener
│ ~~~~~~~~~~
├─ com.example.MyCallback (anonymous)
│ Leaking: UNKNOWN
│ ↓ MyCallback.this$0
├─ com.example.MainActivity
│ Leaking: YES (MainActivity is the leak)
╰
Dans cet exemple, LeakCanary montre que MainActivity est retenue via la chaîne : Application → ArrayList → MainActivity → CallbackWrapper → MyCallback → MainActivity à nouveau. La flèche this$0 indique que la classe anonyme MyCallback a capturé une référence externe à l'Activity. La solution est de faire du callback une référence faible ou de l'annuler dans onDestroy.
LeakCanary affiche également le statut de la fuite pour chaque élément de la chaîne : NO (pas de fuite — élément racine), YES (l'objet doit être détruit), UNKNOWN (statut indéterminé). Le statut UNKNOWN ne signifie pas un problème — c'est un objet intermédiaire que LeakCanary ne peut pas classer de manière concluante.
La transition de la version 1.x à la 2.x a été radicale : les développeurs ont réécrit la bibliothèque de zéro, remplaçant l'ancien analyseur HAHA par leur propre moteur Shark, écrit en Kotlin. Shark est d'un ordre de grandeur plus rapide, nécessite moins de mémoire pour l'analyse et détermine plus précisément les causes profondes des fuites.
| Paramètre | LeakCanary 1.x | LeakCanary 2.x |
|---|---|---|
| Langage de l'analyseur | Java (HAHA — fork du SDK Android) | Kotlin (Shark — moteur propriétaire) |
| Installation | Configuration manuelle d'AppWatcher dans Application | Automatique via ContentProvider |
| Vitesse | 10–30 secondes pour l'analyse du heap dump | 1–5 secondes pour l'analyse du heap dump |
| Performances | Consomme 10–50 Mo de RAM pendant l'analyse | Consomme 2–10 Mo de RAM pendant l'analyse |
L'avantage clé de Shark est qu'il ne charge pas tout le heap dump en mémoire, mais parcourt son graphe de références avec des allocations minimales. Cela rend LeakCanary 2.x utilisable sur des appareils à faible RAM sans risque d'OutOfMemoryError pendant l'analyse.
La version 2.x a également introduit la possibilité d'exporter les heap dumps vers un fichier pour une analyse ultérieure dans Android Studio Memory Profiler. Pour ce faire, activez le paramètre dumpHeapWhenLeakFound dans la configuration d'AppWatcher.
LeakCanary détecte efficacement plusieurs classes de fuites courantes sous Android. La plus fréquente est la fuite via des références statiques à une Activity — les développeurs conservent une référence au contexte de l'Activity dans un singleton, et l'Activity ne peut pas être collectée par le GC après la fin de son cycle de vie.
La deuxième catégorie la plus courante est les fuites via des écouteurs non désenregistrés. Si registerListener a été appelé dans onStart mais que unregisterListener n'a pas été appelé dans onStop/onDestroy, l'objet écouteur est retenu par le système même après la destruction de l'activité. LeakCanary montre clairement quel écouteur et dans quel service système reste vivant.
// Fuite typique : Activity capturée dans un callback de singleton
object AnalyticsManager {
private var callback: ((String) -> Unit)? = null
fun register(callback: (String) -> Unit) {
this.callback = callback // référence forte au callback
}
fun unregister() {
callback = null // N'OUBLIEZ PAS d'appeler dans onDestroy !
}
}
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
AnalyticsManager.register { event ->
logEvent(event) // lambda capture this
}
// si unregister n'est pas appelé dans onDestroy → fuite d'Activity
}
}
La troisième catégorie est les fuites via Fragment dans le BackStack. Si FragmentTransaction.addToBackStack() est appelée sans supprimer le Fragment lors du retour, les anciennes instances de Fragment restent en mémoire. LeakCanary aide à détecter ces fuites cachées dès les premiers stades du développement.
Pour chaque fuite détectée, LeakCanary fournit une description et des recommandations de correction. La version 2.14 a ajouté l'intégration avec Android Lint — la bibliothèque peut créer automatiquement des tâches dans le suivi de problèmes lorsqu'une fuite est détectée dans le CI.
Foire aux questions
Oui, absolument. LeakCanary est ajouté via debugImplementation dans build.gradle, ce qui l'exclut automatiquement des builds de release. Si vous utilisez implementation, la bibliothèque sera incluse dans l'APK de release et montrera les fuites aux utilisateurs finaux — c'est inacceptable.
L'impact sur les performances est minime. LeakCanary ne s'active qu'après le onDestroy d'un composant et n'interfère pas avec le rendu de l'interface ou le traitement des touches. Le seul coût est une courte pause forcée du GC (environ 100 ms) et l'écriture du heap dump en cas de fuite (fractions de seconde).
LeakCanary enregistre automatiquement les heap dumps au format HPROF dans le dossier de l'application. Le fichier peut être exporté via Android Studio : Device File Explorer → data/data/com.example/files/leakcanary/. Pour le visualiser, ouvrez le fichier dans Memory Profiler via Capture → Open Heap Dump.
Oui, depuis la version 2.12, LeakCanary prend entièrement en charge Jetpack Compose. La bibliothèque suit les contextes de Composition et les objets State, détectant automatiquement les fuites dans les fonctions Composable. Aucune configuration séparée n'est nécessaire — cela fonctionne immédiatement.
Les faux positifs sont possibles mais rares. LeakCanary utilise un triple appel au GC avant de déclarer une fuite, ce qui élimine la plupart des faux positifs. Si vous pensez qu'une détection est un faux positif, créez un IgnoredReference pour la classe spécifique dans la configuration.
Résumé
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.
Lisez aussi