Allocation de mémoire entre les processus

La plate-forme Android part du principe que la mémoire disponible est une mémoire gaspillée. Elle tente d'utiliser toute la mémoire disponible en permanence. Par exemple, le système conserve les applications en mémoire après leur fermeture, afin que l'utilisateur puisse y revenir rapidement. C'est pourquoi les appareils Android s'exécutent souvent avec très peu de mémoire libre. La gestion de la mémoire est essentielle pour allouer correctement la mémoire aux processus système importants ainsi qu'à de nombreuses applications utilisateur.

Cette page explique les bases de l'allocation de mémoire par Android pour le système et les applications utilisateur. Elle explique également comment l'OS réagit aux problèmes de mémoire insuffisante.

Types de mémoire

Les appareils Android contiennent trois types de mémoire différents : RAM, zRAM et stockage. Notez que le processeur et le GPU ont accès à la même RAM.

Types de mémoire

Figure 1 : Types de mémoire – RAM, zRAM et stockage

  • La RAM représente le type de mémoire le plus rapide, mais sa taille est généralement limitée. Les appareils haut de gamme disposent généralement de la plus grande quantité de RAM.

  • La zRAM est une partition de RAM utilisée pour l'espace d'échange. Tout élément placé en zRAM est compressé, puis décompressé lorsqu'il est copié en dehors de la zone. Cette partie de la RAM augmente ou diminue à mesure que les pages sont déplacées vers ou retirées de la zone zRAM. Les fabricants d'appareils peuvent en définir la taille maximale.

  • Le stockage contient toutes les données persistantes, telles que le système de fichiers et le code objet inclus pour toutes les applications, bibliothèques et la plate-forme. La capacité du stockage est bien supérieure à celle des deux autres types de mémoire. Sur Android, le stockage n'est pas utilisé pour l'espace d'échange, comme c'est le cas pour d'autres implémentations Linux, car une écriture fréquente peut entraîner l'usure de cette mémoire et raccourcir la durée de vie du support de stockage.

Pages de mémoire

La RAM est divisée en pages. Généralement, chaque page contient 4 Ko de mémoire.

Les pages sont considérées comme disponibles ou utilisées. Les pages disponibles ne sont pas utilisées. Les pages utilisées correspondent à la RAM que le système utilise activement. Elles sont regroupées dans les catégories suivantes :

  • En cache : mémoire sauvegardée par un fichier de stockage (par exemple, du code ou des fichiers mappés en mémoire). Il existe deux types de mémoire cache :
    • Privée : appartenant à un processus et non partagée
      • Propre : copie non modifiée d'un fichier présent sur le stockage ; peut être supprimée par kswapd pour augmenter la mémoire disponible
      • Modifiée : copie altérée du fichier présent sur l'espace de stockage ; peut être déplacée vers la zone zRAM ou compressée dans celle-ci par kswapd pour augmenter la mémoire disponible
    • Partagée : utilisée par plusieurs processus
      • Propre : copie non modifiée du fichier présent sur le stockage ; peut être supprimée par kswapd pour augmenter la mémoire disponible
      • Modifiée : copie altérée du fichier présent sur l'espace de stockage ; permet de réécrire les modifications dans le fichier de stockage afin d'augmenter la mémoire disponible de kswapd, ou en utilisant explicitement msync() ou munmap()
  • Anonyme : mémoire non sauvegardée sur un fichier de stockage (par exemple, allouée par mmap() avec l'option MAP_ANONYMOUS) définie)
    • Modifiée : peut être déplacée/compressée en zRAM par kswapd pour augmenter la mémoire disponible

Les proportions de pages disponibles et utilisées varient au fil du temps, car le système gère activement la mémoire RAM. Les concepts présentés dans cette section sont essentiels pour gérer des situations de mémoire insuffisante. La section suivante de ce document les explique plus en détail.

Gestion de la mémoire faible

Android dispose de deux mécanismes principaux pour gérer les situations de faible mémoire : le daemon de remplacement du noyau et le tueur de mémoire faible.

Daemon de remplacement du noyau

Le daemon de remplacement du noyau (kswapd) fait partie du noyau Linux et convertit la mémoire utilisée en mémoire disponible. Le daemon devient actif lorsque la mémoire disponible sur l'appareil est faible. Le noyau Linux gère les seuils de mémoire libre faibles et élevés. Lorsque la mémoire disponible passe en dessous du seuil minimal, kswapd commence à récupérer de la mémoire. Une fois que la mémoire disponible atteint le seuil élevé, kswapd cesse de récupérer de la mémoire.

kswapd peut récupérer des pages propres en les supprimant, car elles sont sauvegardées par l'espace de stockage et n'ont pas été modifiées. Si un processus tente de traiter une page propre qui a été supprimée, le système copie la page de l'espace de stockage vers la mémoire RAM. Cette opération est appelée pagination à la demande.

La page propre sauvegardée a été supprimée

Figure 2 : Page propre, sauvegardée sur le stockage, supprimée

kswapd peut déplacer vers la ZRAM des pages modifiées privées mises en cache de manière anonyme, où elles sont compressées. Cela libère de la mémoire disponible en RAM (pages disponibles). Si un processus tente de joindre une page modifiée en zRAM, elle est non compressée et replacée dans la RAM. Si le processus associé à une page compressée est arrêté, la page est supprimée de la zone zRAM.

Si la quantité de mémoire disponible passe en dessous d'un certain seuil, le système commence à arrêter les processus.

Page modifiée déplacée vers la zRAM et compressée

Figure 3 : Page modifiée déplacée vers la zRAM et compressée

Tueur de mémoire faible

Souvent, kswapd ne peut pas libérer suffisamment de mémoire pour le système. Dans ce cas, le système utilise onTrimMemory() pour avertir l'application que la mémoire est faible et qu'elle doit réduire ses allocations. Si ce n'est pas suffisant, le noyau commence à éliminer les processus pour libérer de la mémoire. Pour ce faire, il utilise le tueur de mémoire faible (LMK).

Pour identifier le processus à arrêter, le LMK utilise un score de « mémoire insuffisante » appelé oom_adj_score afin de hiérarchiser les processus en cours d'exécution. Les processus avec un score élevé sont arrêtés en premier. Les applications en arrière-plan doivent d'abord être supprimées, puis les processus système en fin de vie. Le tableau suivant recense les catégories de score LMK, du plus élevé au plus bas. Les éléments de la catégorie ayant obtenu le meilleur score, à la ligne 1, seront supprimés en premier :

Processus Android, en partant du score le plus élevé

Figure 4 : Processus Android, en partant du score le plus élevé

Voici les descriptions des différentes catégories incluses dans le tableau ci-dessus :

  • Applications en arrière-plan : applications exécutées précédemment et qui ne sont pas actives actuellement. Le LMK supprime d'abord les applications en arrière-plan, en commençant par celle dont le oom_adj_score est le plus élevé.

  • Application précédente : dernière application utilisée en arrière-plan. L'application précédente a une priorité plus élevée (un score plus faible) que les applications en arrière-plan, car il est plus probable que l'utilisateur y retourne par rapport à l'une des applications en arrière-plan.

  • Application d'accueil : il s'agit de l'application de lancement. Si vous l'arrêtez, le fond d'écran disparaîtra.

  • Services : les services sont démarrés par des applications et peuvent inclure la synchronisation ou l'importation dans le cloud.

  • Applications perceptibles : applications non essentielles qui sont perceptibles par l'utilisateur d'une manière ou d'une autre, par exemple en exécutant un processus de recherche qui affiche une petite interface utilisateur, ou encore un lecteur de musique.

  • Application au premier plan : application en cours d'utilisation. Tuer l'application au premier plan ressemble à un plantage qui peut indiquer à l'utilisateur que l'appareil rencontre un problème.

  • Persistants (services) : services principaux de l'appareil, tels que la téléphonie et le Wi-Fi.

  • Système : processus système. Lorsque ces processus sont tués, le téléphone semble parfois redémarrer.

  • Natifs : processus de très bas niveau utilisés par le système (par exemple, kswapd).

Les fabricants d'appareils peuvent modifier le comportement du LMK.

Calcul de l'espace mémoire utilisé

Le noyau effectue le suivi de toutes les pages de mémoire du système.

Pages utilisées par différents processus

Figure 5 : Pages utilisées par différents processus

Pour déterminer la quantité de mémoire utilisée par une application, le système doit tenir compte des pages partagées. Les applications qui accèdent au même service ou à la même bibliothèque partagent des pages de mémoire. Par exemple, les services Google Play et une application de jeu peuvent partager un service de localisation. Il est donc difficile de déterminer la quantité de mémoire appartenant au service en question par rapport à chaque application.

Pages partagées par deux applications

Figure 6 : Pages partagées par deux applications (milieu)

Pour déterminer l'espace mémoire utilisé par une application, vous pouvez utiliser l'une des métriques suivantes :

  • Taille de l'ensemble résident (RSS) : nombre de pages partagées et non partagées utilisées par l'application
  • Taille de l'ensemble proportionnel (PSS) : nombre de pages non partagées utilisées par l'application, avec une répartition équitable des pages partagées (par exemple, si trois processus partagent 3 Mo de ressources, chaque processus reçoit 1 Mo à l'échelle PSS)
  • Taille de l'ensemble unique (USS) : nombre de pages non partagées utilisées par l'application (les pages partagées ne sont pas incluses)

L'indicateur PSS est utile pour le système d'exploitation lorsque celui-ci désire connaître la quantité de mémoire utilisée par tous les processus, car les pages ne sont pas comptabilisées plusieurs fois. Le calcul PSS prend beaucoup de temps, car le système a besoin de déterminer les pages partagées et le nombre de processus. La mesure RSS ne fait pas la distinction entre les pages partagées et non partagées, ce qui accélère le calcul. Cette métrique est également préférable pour suivre les modifications apportées à l'allocation de mémoire.

Ressources supplémentaires