Aperçu de la gestion de la mémoire

Le Runtime Android (ART) et la machine virtuelle Dalvik utilisent la pagination et le mappage de la mémoire (mmapping) pour gérer la mémoire. Cela signifie que toute mémoire qu’une app modifie – que ce soit en allouant de nouveaux objets ou en touchant des pages mmappées – reste résidente en RAM et ne peut pas être paginée. La seule façon de libérer la mémoire d’une application est de libérer les références d’objets que l’application détient, rendant ainsi la mémoire disponible pour le ramasseur de déchets. Ceci à une exception près : tout fichier mis en mémoire sans modification, comme le code, peut être paginé hors de la RAM si le système veut utiliser cette mémoire ailleurs.

Cette page explique comment Android gère les processus des apps et l’allocation de mémoire. Pour plus d’informations sur la manière de gérer plus efficacement la mémoire dans votre app, consultez la section Gérer la mémoire de votre app.

Collecte de déchets

Un environnement de mémoire géré, comme la machine virtuelle ART ou Dalvik, garde la trace de chaque allocation de mémoire. Dès qu’il détermine qu’un morceau de mémoire n’est plus utilisé par le programme, il le libère pour le remettre dans le tas, sans aucune intervention du programmeur. Le mécanisme de récupération de la mémoire inutilisée dans un environnement de mémoire géré est connu sous le nom de ramassage des déchets. Le ramassage des ordures a deux objectifs : trouver les objets de données d’un programme auxquels on ne pourra plus accéder à l’avenir ; et récupérer les ressources utilisées par ces objets.

Le tas de mémoire d’Android est un tas générationnel, ce qui signifie qu’il existe différents seaux d’allocations qu’il suit, en fonction de la durée de vie et de la taille prévues d’un objet alloué. Par exemple, les objets récemment alloués appartiennent à la génération Young. Lorsqu’un objet reste actif suffisamment longtemps, il peut être promu à une génération plus ancienne, suivie d’une génération permanente.

Chaque génération de tas a sa propre limite supérieure dédiée à la quantité de mémoire que les objets qui s’y trouvent peuvent occuper. Chaque fois qu’une génération commence à se remplir, le système exécute un événement de garbage collection pour tenter de libérer de la mémoire. La durée du garbage collection dépend de la génération d’objets qu’il collecte et du nombre d’objets actifs dans chaque génération.

Même si le garbage collection peut être assez rapide, il peut néanmoins affecter les performances de votre app. Vous ne contrôlez généralement pas le moment où un événement de garbage collection se produit depuis votre code. Le système dispose d’un ensemble de critères de fonctionnement pour déterminer quand effectuer le garbage collection. Lorsque les critères sont satisfaits, le système arrête l’exécution du processus et commence le ramassage des déchets. Si le ramassage des déchets se produit au milieu d’une boucle de traitement intensif, comme une animation ou la lecture d’une musique, cela peut augmenter le temps de traitement. Cette augmentation peut potentiellement pousser l’exécution du code dans votre app au-delà du seuil recommandé de 16 ms pour un rendu efficace et fluide des images.

En outre, votre flux de code peut effectuer des types de travaux qui forcent les événements de garbage collection à se produire plus souvent ou les font durer plus longtemps que la normale. Par exemple, si vous allouez plusieurs objets dans la partie la plus interne d’une boucle for pendant chaque image d’une animation de mélange alpha, vous pourriez polluer votre tas de mémoire avec beaucoup d’objets. Dans cette circonstance, le ramasseur d’ordures exécute plusieurs événements de ramassage d’ordures et peut dégrader les performances de votre application.

Pour plus d’informations générales sur le ramassage d’ordures, voir Ramassage d’ordures.

Partager la mémoire

Afin de faire tenir tout ce dont il a besoin dans la RAM, Android essaie de partager les pages de RAM entre les processus. Il peut le faire de la manière suivante :

  • Chaque processus d’application est bifurqué d’un processus existant appelé Zygote. Le processus Zygote démarre lorsque le système démarre et charge le code et les ressources du framework commun (comme les thèmes d’activité). Pour lancer un nouveau processus d’application, le système fait bifurquer le processus Zygote, puis charge et exécute le code de l’application dans le nouveau processus. Cette approche permet de partager la plupart des pages de RAM allouées au code et aux ressources du framework entre tous les processus d’app.
  • La plupart des données statiques sont mmappées dans un processus. Cette technique permet aux données d’être partagées entre les processus, et permet également de les sortir en pagination lorsque cela est nécessaire. Les exemples de données statiques incluent : Le code Dalvik (en le plaçant dans un fichier .odex prélié pour un mmapping direct), les ressources de l’app (en concevant la table des ressources comme une structure pouvant être mmappée et en alignant les entrées zip de l’APK), et les éléments de projet traditionnels comme le code natif dans des fichiers .so.
  • En de nombreux endroits, Android partage la même RAM dynamique entre les processus en utilisant des régions de mémoire partagée allouées explicitement (soit avec ashmem ou gralloc). Par exemple, les surfaces de fenêtre utilisent la mémoire partagée entre l’app et le compositeur d’écran, et les tampons de curseur utilisent la mémoire partagée entre le fournisseur de contenu et le client.

En raison de l’utilisation intensive de la mémoire partagée, la détermination de la quantité de mémoire utilisée par votre app nécessite de la prudence. Les techniques permettant de déterminer correctement l’utilisation de la mémoire de votre app sont abordées dans Enquêter sur votre utilisation de la RAM.

Allocation et récupération de la mémoire de l’app

Le tas Dalvik est contraint à une seule plage de mémoire virtuelle pour chaque processus d’app. Cela définit la taille logique du tas, qui peut croître selon ses besoins mais seulement jusqu’à une limite que le système définit pour chaque app.

La taille logique du tas n’est pas la même que la quantité de mémoire physique utilisée par le tas. Lorsqu’il inspecte le tas de votre app, Android calcule une valeur appelée Proportional Set Size (PSS), qui tient compte des pages sales et propres partagées avec d’autres processus – mais uniquement dans une quantité proportionnelle au nombre d’apps qui partagent cette RAM. Ce total (PSS) est ce que le système considère comme l’empreinte de votre mémoire physique. Pour plus d’informations sur le PSS, consultez le guide Investigating Your RAM Usage.

Le tas Dalvik ne compacte pas la taille logique du tas, ce qui signifie qu’Android ne défragmente pas le tas pour fermer de l’espace. Android ne peut réduire la taille logique du tas que lorsqu’il y a de l’espace inutilisé à la fin du tas. Cependant, le système peut toujours réduire la mémoire physique utilisée par le tas. Après le ramassage des ordures, Dalvik parcourt le tas et trouve les pages inutilisées, puis renvoie ces pages au noyau en utilisant madvise. Ainsi, les allocations et désallocations appariées de gros morceaux devraient permettre de récupérer toute (ou presque toute) la mémoire physique utilisée. Cependant, la récupération de la mémoire à partir de petites allocations peut être beaucoup moins efficace, car la page utilisée pour une petite allocation peut encore être partagée avec quelque chose d’autre qui n’a pas encore été libéré.

Restreindre la mémoire des apps

Pour maintenir un environnement multitâche fonctionnel, Android fixe une limite stricte à la taille du tas pour chaque app. La limite exacte de la taille du tas varie selon les appareils, en fonction de la quantité de RAM dont l’appareil dispose globalement. Si votre app a atteint la capacité du tas et tente d’allouer plus de mémoire, elle peut recevoir un OutOfMemoryError.

Dans certains cas, vous pouvez interroger le système pour déterminer exactement l’espace de tas dont vous disposez sur l’appareil actuel – par exemple, pour déterminer la quantité de données qu’il est sûr de conserver dans un cache. Vous pouvez interroger le système pour obtenir ce chiffre en appelant getMemoryClass(). Cette méthode renvoie un nombre entier indiquant le nombre de mégaoctets disponibles pour le tas de votre app.

Changer d’apps

Lorsque les utilisateurs passent d’une app à l’autre, Android conserve les apps qui ne sont pas au premier plan – c’est-à-dire qui ne sont pas visibles pour l’utilisateur ou qui exécutent un service au premier plan comme la lecture de musique – dans un cache. Par exemple, lorsqu’un utilisateur lance une application pour la première fois, un processus est créé pour celle-ci ; mais lorsque l’utilisateur quitte l’application, ce processus ne se termine pas. Le système conserve le processus en mémoire cache. Si l’utilisateur revient plus tard sur l’app, le système réutilise le processus, rendant ainsi le passage de l’app plus rapide.

Si votre app a un processus en cache et qu’elle conserve des ressources dont elle n’a pas besoin actuellement, alors votre app – même si l’utilisateur ne l’utilise pas – affecte les performances globales du système. Lorsque le système est à court de ressources, comme la mémoire, il tue les processus dans le cache. Le système tient également compte des processus qui conservent le plus de mémoire et peut les terminer pour libérer de la RAM.

Note : Moins votre app consomme de mémoire lorsqu’elle est dans le cache, plus elle a de chances de ne pas être tuée et de pouvoir reprendre rapidement. Cependant, en fonction des exigences instantanées du système, il est possible que les processus mis en cache soient terminés à tout moment, quelle que soit leur utilisation des ressources.

Pour plus d’informations sur la façon dont les processus sont mis en cache lorsqu’ils ne s’exécutent pas au premier plan et sur la façon dont Android décide de ceux qui peuvent être tués, consultez le guide Processus et fils de discussion.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *