Fuite mémoire et gonflement « ce que c'est, causes et comment éviter

Auteur : IT Sectr Publié le : 2026-07-28 Temps de lecture : 10 min

Fuite mémoire — l'un des problèmes les plus insidieux du développement mobile. L'utilisation mémoire de l'application augmente régulièrement jusqu'à atteindre la limite fixée par l'OS, suivie d'une OutOfMemoryError ou d'une terminaison forcée. Selon Square Engineering, environ 40% des applications Android ont au moins une fuite mémoire qui ne peut être détectée que par profilage. Examinons les causes et les méthodes pour prévenir la croissance mémoire.

Points clés

  • Accessibilité GC — un objet n'est pas supprimé s'il existe une référence active depuis l'ensemble racine
  • Références statiques à Activity ou Context — la cause la plus fréquente de fuites sous Android
  • LeakCanary — l'outil standard de détection automatique des fuites sous Android
  • WeakReference — une solution pour les références qui ne doivent pas entraver le ramasse-miettes
  • Composants Lifecycle-aware annulent automatiquement les abonnements à la destruction de la vue

Qu'est-ce qu'une fuite mémoire et le gonflement d'application ?

Fuite mémoire — une situation où un objet qui n'est plus nécessaire à l'application continue d'être conservé dans le tas parce qu'une référence active depuis l'ensemble racine (GC Root) pointe encore vers lui. Le ramasse-miettes considère cet objet comme vivant et ne le supprime pas.

Gonflement mémoire — un problème plus large où l'application consomme plus de mémoire que nécessaire pour effectuer ses tâches courantes. Causes : mise en cache excessive, duplication d'objets, structures de données sous-optimales et fragmentation du tas.

Sous Android, chaque application dispose d'un tas limité (généralement 64–512 Mo selon l'appareil et la version de l'OS). Sous iOS, la limite est moins stricte, mais le système envoie un avertissement mémoire à l'approche de la limite.

CaractéristiqueAndroidiOS
Limite du tas64–512 Mo (selon l'appareil)Implicite (système)
Ramasse-miettesART (Concurrent, Compact)ARC (Comptage automatique de références)
Mécanisme de fuiteRéférences GC RootCycles de rétention (cycles de références fortes)
RésultatOutOfMemoryErrorAvertissement mémoire → terminaison

Selon le Facebook Engineering Blog, les fuites mémoire causent ~15% des rapports de crash dans les applications mobiles. Sous Android, s'ajoutent les ANR dus aux pauses fréquentes du GC en cas de mémoire insuffisante.

Motifs courants de fuites mémoire sous Android et iOS

Référence statique à Activity — une fuite classique sous Android. Si un champ statique ou un singleton conserve une référence à une Activity, elle ne sera pas collectée par le GC même après finish() tant que le singleton est vivant. Une Activity est un objet lourd contenant une hiérarchie de vues, des ressources et un Context.

kotlin
object LeakHolder {
    var activityRef: Activity ?= null // leak: static reference to Activity
}

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle ?= null) {
        super.onCreate(savedInstanceState)
        LeakHolder.activityRef = this // ❌ MainActivity will never be GC'd
    }
}

Classes anonymes et lambdas — retiennent implicitement une référence à la classe externe. Si un Runnable ou Callback est passé à un service externe et que l'Activity est détruite, l'objet de la classe anonyme reste dans la file et empêche l'Activity d'être collectée.

  • Handler avec délai — si l'Activity est détruite mais que Handler.postDelayed n'a pas encore été exécuté, l'Activity fuit
  • Thread et AsyncTask — lors de la rotation de l'écran, l'Activity est recréée tandis que l'ancien Thread continue de conserver une référence à l'ancienne Activity
  • Retrofit/Callback — un Callback anonyme conserve une référence au presenter ou au fragment
  • Observateurs — abonnements LiveData ou RxJava sans désabonnement à onDestroy

Sous iOS, le problème principal est les cycles de rétention : deux objets maintiennent des références fortes l'un vers l'autre, et l'ARC ne peut mettre à zéro le compteur de références d'aucun des deux. Un cas typique : une closure qui capture self fortement, et self qui conserve une référence à la closure.

Comment détecter les fuites mémoire ?

LeakCanary — une bibliothèque de Square pour la détection automatique des fuites sous Android. Après la destruction d'une Activity ou d'un Fragment, elle vérifie si l'objet a été collecté par le GC. Sinon, elle fait un heap dump et affiche la trace de la fuite.

kotlin
// LeakCanary 2.x — auto-integration via Application
class ExampleApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        // LeakCanary auto-installs in debug build
        // via ContentProvider — zero code setup
    }
}

// Force check invocation
AppWatcher.objectWatcher.watch(watchedObject, "leak description")

Android Studio Profiler — un outil intégré de surveillance mémoire en temps réel. Il permet d'enregistrer un heap dump, de trouver des objets suspects (Retained Size > 1 Mo) et de tracer le chemin racine GC jusqu'à chaque objet.

Pour iOS, utilisez le Xcode Memory Graph Debugger. Il visualise le graphe d'objets en mémoire, montre les cycles de rétention et permet de détecter instantanément les références circulaires. Instruments > Allocations est également disponible pour la surveillance à long terme.

Stratégies de prévention

WeakReference — un mécanisme de base pour les références qui ne doivent pas interférer avec le ramasse-miettes. Si le GC décide de collecter un objet, WeakReference retourne null. Il est utilisé pour les callbacks, les écouteurs et les références aux composants d'interface depuis des threads d'arrière-plan.

Composants Lifecycle-aware — une approche architecturale implémentée dans Android Jetpack (Lifecycle, LiveData, Flow, coroutines). Les abonnements sont automatiquement annulés à onDestroy, éliminant la principale classe de fuites.

kotlin
class MyViewModel : ViewModel() {
    private val _data = MutableLiveData<List<User>>()
    val data: LiveData<List<User>> get() = _data

    fun loadData() {
        viewModelScope.launch {
            val result = repository.fetchData()
            _data.postValue(result)
            // coroutine auto-cancels on onCleared()
        }
    }
}

viewModelScope et lifecycleScope — des CoroutineScope intégrés dans Android qui sont annulés à l'événement de cycle de vie correspondant. Cela élimine les fuites via les coroutines — le scénario le plus courant dans le développement Android moderne.

  • N'utilisez pas de références statiques à Context, Activity, View ou Fragment
  • Annulez tous les abonnements RxJava dans disposeBag / CompositeDisposable à onDestroy
  • Utilisez [weak self] / [unowned self] dans les closures iOS pour prévenir les cycles de rétention
  • Vérifiez Bitmap et les objets volumineux — ils doivent être recyclés ou nullifiés

Outils de profilage mémoire

Memory Profiler dans Android Studio — l'outil principal de surveillance du tas. Il montre les allocations en direct, les instantanés du tas et les comptes d'objets par type. Il permet d'enregistrer un dump et de l'analyser dans MAT (Memory Analyzer Tool) pour trouver des objets suspects.

Eclipse MAT — un analyseur de heap dump de bureau. Après avoir chargé un fichier HPROF depuis Android Studio, MAT construit un arbre dominant, montre la taille de rétention de chaque objet et propose une analyse automatique des fuites suspectes via le Leak Suspects Report.

Xcode Memory Graph — un débogueur visuel de cycles de rétention. En cliquant sur le bouton Memory Graph Debugger, Xcode arrête l'application, construit un graphe complet d'objets en mémoire et surligne les cycles de rétention en rouge.

OutilPlateformeFonctionnalité
LeakCanaryAndroidDétection automatique des fuites après destroy
Memory ProfilerAndroid StudioHeap dump + allocations en direct
Eclipse MATAndroidArbre dominant, Leak Suspects Report
Memory GraphiOS (Xcode)Visualiseur de cycles de rétention

Selon Google I/O 2023, les applications utilisant LeakCanary dans les builds de débogage réduisent les crashes liés à la mémoire de 30–50% dans les 2 premiers mois suivant l'adoption. Il est recommandé d'ajouter LeakCanary dès la phase d'intégration du projet.

Questions fréquentes

Quelle est la différence entre une fuite mémoire et le gonflement ?

Fuite — objets inaccessibles au code mais non collectés par le GC en raison de références actives. Gonflement — l'application conserve des objets logiquement nécessaires mais en quantité excessive (par exemple, un cache de 50 Mo dans une application de 80 Mo en cours d'exécution). Le gonflement se résout architecturalement, la fuite par une gestion correcte des références.

Comment LeakCanary trouve-t-il les fuites ?

LeakCanary utilise ObjectWatcher — après onDestroy() d'une Activity, il crée une WeakReference sur l'Activity et exécute le GC. Si la WeakReference n'est pas effacée après 5 secondes, LeakCanary fait un heap dump, analyse la chaîne de référence la plus courte du GC Root à l'objet et affiche la pile exacte de la fuite avec le fichier et la ligne de code.

Pourquoi Bitmap provoque-t-il souvent OutOfMemoryError ?

Bitmap occupe de la mémoire en dehors du tas Java dans la mémoire native (tas natif). La taille d'un Bitmap = largeur × hauteur × 4 octets (ARGB_8888). Une photo de 12 MP (4000×3000) prend 48 Mo. Android ne peut pas toujours libérer la mémoire native à temps, donc l'accumulation de plusieurs Bitmaps entraîne OOM même avec un tas Java suffisant.

Qu'est-ce qu'un cycle de rétention sous iOS ?

Cycle de rétention — une situation dans l'ARC où deux objets maintiennent des références fortes l'un vers l'autre et le compteur de références n'atteint jamais zéro. Un exemple typique : un ViewController avec une référence forte à une closure, et la closure capture self fortement. Solution : utilisez [weak self] ou [unowned self] dans les closures.

Quelle est la taille maximale du tas sous Android ?

La taille du tas dépend de l'appareil et de la version d'Android. Pour les anciens appareils (API 15–24) — 64–128 Mo. Pour les modernes (API 25+) — 256–512 Mo. La valeur exacte peut être obtenue via ActivityManager.getMemoryClass(). Pour les grandes applications (jeux, éditeurs), largeHeap=true dans le manifeste fournit jusqu'à 1 Go.

Résumé

  • Fuite mémoire — un objet non collecté par le GC en raison d'une référence active de l'ensemble racine ; gonflement — consommation excessive de mémoire sans fuites explicites
  • Références statiques à Activity, Context ou View — la cause numéro un des fuites sous Android ; solution — WeakReference ou Application Context
  • Classes anonymes et lambdas retiennent implicitement une référence à la classe externe ; les callbacks non annulés sont la deuxième cause la plus fréquente
  • LeakCanary — le standard de détection automatique des fuites sous Android ; l'intégration prend 5 minutes et réduit le taux de crash de 30–50%
  • lifecycleScope et viewModelScope annulent automatiquement les coroutines à la destruction, éliminant toute une classe de fuites
  • Cycles de rétention sous iOS se résolvent avec weak/unowned self dans les closures et les délégués
  • Profilez la mémoire au moins une fois par sprint — un heap dump avec MAT ou Memory Graph devrait faire partie de la revue de code

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