Limiteur de mémoire

Android 17 et versions ultérieures incluent le limiteur de mémoire, un service système qui surveille et limite l'utilisation de la mémoire des processus d'application à l'aide de Linux cgroup v2. Le limiteur de mémoire empêche les applications individuelles de consommer une quantité disproportionnée de mémoire système, ce qui réduit la pression globale sur la mémoire et évite les arrêts agressifs des processus critiques de premier plan et mis en cache en cas de mémoire insuffisante.

Mécanisme

Le limiteur de mémoire s'intègre au service Activity Manager (AMS) pour suivre les événements du cycle de vie des processus et les changements d'état. Le limiteur de mémoire applique des limites de mémoire à l'aide de la hiérarchie cgroup v2 du noyau Linux.

Pour que le limiteur de mémoire soit compatible, le noyau de l'appareil doit activer cgroup v2 et le contrôleur memory. Le service s'appuie spécifiquement sur les attributs suivants :

memory.high
Une limite souple. En cas de dépassement, le processus est limité et le noyau tente de récupérer de manière proactive les pages de ce processus.
memory.swap.max
Limite la quantité maximale d'espace d'échange (comme ZRAM) que le processus peut utiliser.

Impact sur les applications

Les applications qui fonctionnent dans les limites de mémoire ne sont pas affectées par le limiteur de mémoire.

Lorsqu'une application dépasse sa limite memory.high, le noyau supprime la mémoire propre de l'application basée sur des fichiers et échange sa mémoire anonyme inactive pour maintenir l'application dans la limite. En raison de cette activité d'éviction et d'échange de pages, l'application peut subir une limitation temporaire de l'exécution, mais elle continue de s'exécuter.

Dans des situations extrêmes, comme une fuite de mémoire non contrôlée où l'application continue d'allouer de la mémoire anonyme après l'épuisement de l'espace d'échange, l'application ne parvient pas à allouer de la mémoire et est arrêtée.

Surveillance des processus

Par défaut, le limiteur de mémoire surveille les processus d'application (UID >= 10000). Les processus système essentiels sont exemptés afin de préserver la stabilité du système.

Le Memory Limiter attribue des limites de mémoire en fonction de l'état de visibilité du processus :

  • Processus visibles : processus hébergeant une UI visible par l'utilisateur (par exemple, l'activité actuellement sélectionnée ou une interaction active). Comme l'affichage d'une UI nécessite un ensemble de travail plus important pour le rendu et les pipelines graphiques, les processus visibles bénéficient d'une limite de mémoire plus généreuse.

  • Processus non visibles : processus effectuant des tâches en arrière-plan sans présenter d'UI (par exemple, les services d'arrière-plan ou les broadcast receivers). Comme ils n'affichent pas de composants d'interface utilisateur, ils sont soumis à une limite plus stricte.

Le tableau suivant met en correspondance des états de processus spécifiques avec des classifications de limites de mémoire :

État du processusLimite de mémoire
PERSISTENTPas de restriction
PERSISTENT_UIPas de restriction
TOPVisible
BOUND_TOPVisible
FOREGROUND_SERVICENon visible
BOUND_FOREGROUND_SERVICENon visible
IMPORTANT_FOREGROUNDVisible
IMPORTANT_BACKGROUNDNon visible
TRANSIENT_BACKGROUNDNon visible
BACKUPNon visible
SERVICENon visible
RECEIVERNon visible
TOP_SLEEPINGVisible
HEAVY_WEIGHTNon visible
HOMENon visible
LAST_ACTIVITYNon visible
CACHED_ACTIVITYEn cache
CACHED_ACTIVITY_CLIENTEn cache
CACHED_RECENTEn cache
CACHED_EMPTYEn cache

Dans l'état mis en cache, les processus sont figés, puis récupérés au maximum.

Lorsqu'un processus dépasse la limite memory.high qui lui est attribuée, le limiteur de mémoire détecte l'événement et peut déclencher des actions de diagnostic, telles que la capture d'un profil de mémoire ou la journalisation d'une anomalie dans statsd.

Configuration

Dans Android 17 et versions ultérieures, le limiteur de mémoire est configuré à l'aide du fichier de configuration de la plate-forme standard sur la partition système à l'emplacement /system/etc/memory-limiter-config.xml.

Format XML

Le fichier de configuration suit le schéma défini dans memory-limiter-config.xsd. Le fichier définit plusieurs ensembles de limites classés par mémoire disponible. Le service sélectionne l'ensemble de limites correspondant le plus élevé en fonction de la RAM disponible de l'appareil. Toutes les valeurs de mémoire sont spécifiées en mébioctets (Mio).

<MemoryLimiterConfig>
  <version>1</version>
  <configList>
    <!-- RAM boundaries account for MemTotal excluding carve-outs. -->
    <limitSet>
      <!-- Limits for 16GB RAM device [14336, 18432): 10G/5G/5G/5G -->
      <minimumRequiredMemTotal>14336</minimumRequiredMemTotal>
      <maximumAllowedMemTotal>18432</maximumAllowedMemTotal>
      <memVisible>10240</memVisible>
      <memNotVisible>5120</memNotVisible>
      <swapVisible>5120</swapVisible>
      <swapNotVisible>5120</swapNotVisible>
    </limitSet>
    <limitSet>
      <!-- Limits for 12GB RAM device [9216, 14336): 8G/4G/4G/4G -->
      <minimumRequiredMemTotal>9216</minimumRequiredMemTotal>
      <maximumAllowedMemTotal>14336</maximumAllowedMemTotal>
      <memVisible>8192</memVisible>
      <memNotVisible>4096</memNotVisible>
      <swapVisible>4096</swapVisible>
      <swapNotVisible>4096</swapNotVisible>
    </limitSet>
    <limitSet>
      <!-- Limits for 8GB RAM device [6800, 9216): 5G/3G/3G/3G -->
      <minimumRequiredMemTotal>6800</minimumRequiredMemTotal>
      <maximumAllowedMemTotal>9216</maximumAllowedMemTotal>
      <memVisible>5120</memVisible>
      <memNotVisible>3072</memNotVisible>
      <swapVisible>3072</swapVisible>
      <swapNotVisible>3072</swapNotVisible>
    </limitSet>
    <limitSet>
      <!-- Limits for 6GB RAM device [4800, 6800): 4G/2G/2G/2G -->
      <minimumRequiredMemTotal>4800</minimumRequiredMemTotal>
      <maximumAllowedMemTotal>6800</maximumAllowedMemTotal>
      <memVisible>4096</memVisible>
      <memNotVisible>2048</memNotVisible>
      <swapVisible>2048</swapVisible>
      <swapNotVisible>2048</swapNotVisible>
    </limitSet>
    <limitSet>
      <!-- Limits for 4GB RAM device [3200, 4800): 2G/1G/1G/1G -->
      <minimumRequiredMemTotal>3200</minimumRequiredMemTotal>
      <maximumAllowedMemTotal>4800</maximumAllowedMemTotal>
      <memVisible>2048</memVisible>
      <memNotVisible>1024</memNotVisible>
      <swapVisible>1024</swapVisible>
      <swapNotVisible>1024</swapNotVisible>
    </limitSet>
  </configList>
</MemoryLimiterConfig>
version
Entier positif identifiant la version de la configuration. Il doit s'agir de 1.
minimumRequiredMemTotal

MemTotal système minimal (en Mio, plancher inclusif) requis pour que cet ensemble de limites s'applique. Le service évalue cette valeur par rapport à la mémoire totale disponible signalée par le noyau dans /proc/meminfo.

Comprendre les limites et les allocations de mémoire : la valeur du kernel MemTotal reflète la DRAM physique accessible au système d'exploitation après déduction des allocations de mémoire matérielle et du micrologiciel. Les carve-outs réservent de la RAM pour des composants matériels spécialisés tels que le GPU, le modem de bande de base, le processeur de signal d'image (ISP) de l'appareil photo, les environnements d'exécution sécurisés et les hyperviseurs, consommant généralement entre 500 Mio et plus de 2 Gio de mémoire physique.

Étant donné que MemTotal est inférieur à la DRAM physique annoncée de l'appareil, les seuils minimumRequiredMemTotal incluent une marge pour ces exclusions. Par exemple, une configuration ciblant les appareils de 16 Go spécifie un seuil de 14336 (14 Gio) plutôt que de 16384 (16 Gio), et un niveau pour les appareils de 12 Go spécifie un seuil de 9216 (9 Gio). Cela permet de s'assurer que chaque appareil correspond au niveau prévu, quelles que soient les variations de carve-out au niveau de la carte.

maximumAllowedMemTotal

MemTotal système maximal (en Mio, plafond exclusif) pour que cet ensemble de limites s'applique. Si MemTotal est supérieur ou égal à ce plafond, la limite définie ne s'applique pas. Pour les appareils disposant de 18 432 Mio ou plus, ou pour les appareils disposant de moins de 3 200 Mio, le limiteur de mémoire n'applique pas de limites, ce qui laisse les processus sans contrainte.

memVisible

Limite de mémoire logicielle (memory.high) en Mio appliquée aux processus visibles.

memNotVisible

Limite de mémoire logicielle (memory.high) en Mio appliquée aux processus non visibles.

swapVisible

Limite d'espace d'échange (memory.swap.max) en Mio appliquée aux processus visibles.

swapNotVisible

Limite d'espace d'échange (memory.swap.max) en Mio appliquée aux processus non visibles.

Limites standards de la plate-forme

Dans Android 17 et versions ultérieures, la plate-forme spécifie des limites de mémoire et d'échange standards dans /system/etc/memory-limiter-config.xml pour les niveaux de RAM physique courants à l'aide d'intervalles de plage [floor, ceiling) :

RAM physique cible Plage MemTotal [plancher, plafond)
(minimumRequiredMemTotal,
maximumAllowedMemTotal)
Limite de mémoire visible
(memVisible)
Limite de mémoire non visible
(memNotVisible)
Limite d'échange visible
(swapVisible)
Limite d'échange non visible
(swapNotVisible)
16 Go [14336, 18432) Mio 10 240 Mio (10 Gio) 5 120 Mio (5 Gio) 5 120 Mio (5 Gio) 5 120 Mio (5 Gio)
12 Go [9216, 14336) Mio 8 192 Mio (8 Gio) 4 096 Mio (4 Gio) 4 096 Mio (4 Gio) 4 096 Mio (4 Gio)
8 Go [6800, 9216) Mio 5 120 Mio (5 Gio) 3 072 Mio (3 Gio) 3 072 Mio (3 Gio) 3 072 Mio (3 Gio)
6 Go [4800, 6800) Mio 4 096 Mio (4 Gio) 2 048 Mio (2 Gio) 2 048 Mio (2 Gio) 2 048 Mio (2 Gio)
4 Go [3200, 4800) Mio 2 048 Mio (2 Gio) 1 024 Mio (1 Gio) 1 024 Mio (1 Gio) 1 024 Mio (1 Gio)

Principes des limites de mémoire

La configuration du limiteur de mémoire est conçue autour des principes de plate-forme suivants :

  • Cohérence de l'écosystème et compatibilité des applications : des limites de mémoire cohérentes sur tous les appareils garantissent des performances d'application prévisibles dans l'écosystème Android. Les applications sont développées et testées en fonction des attentes standards en matière de mémoire. Le maintien de limites de plate-forme uniformes évite les limitations inattendues ou les arrêts prématurés.

  • Allocation proportionnelle des ressources : les limites de la plate-forme sont calibrées en fonction de la capacité de RAM physique :

    • Processus visibles : environ la moitié ou les deux tiers de la RAM physique totale sont alloués pour prendre en charge les charges de travail d'UI, de composition et d'affichage actives.
    • Processus non visibles : environ un quart à un tiers de la RAM physique totale est alloué aux tâches en arrière-plan.
  • Applicabilité universelle : les limites s'appliquent de manière uniforme à tous les processus d'application sur l'appareil (UID >= 10000), y compris les applications système et OEM préinstallées. Le limiteur de mémoire n'est pas compatible avec les listes d'autorisation permettant d'exempter des applications spécifiques, ce qui garantit une gestion équitable de la mémoire pour toutes les applications. Pour configurer des seuils de protection memory.min à réglage automatique sur les daemons interactifs critiques et des plafonds memory.high plus stricts et calibrés en fonction de la charge de travail sur les applications et services système individuels, consultez Budgets de mémoire système.

  • Absence d'API de requête d'exécution : sur Android 17 et versions ultérieures, les applications ne peuvent pas interroger de manière programmatique les limites de mémoire qui leur sont attribuées au moment de l'exécution. Les limites standards de la plate-forme sont définies avec des marges généreuses afin que les applications bien conçues fonctionnent sans contrainte lors d'une utilisation normale.

  • Dynamique de récupération et de remplacement du noyau : lorsqu'un processus d'application approche de sa limite memory.high, le noyau Linux dirige la récupération de mémoire spécifiquement vers ce cgroup de processus. Cela inclut la suppression des pages inactives sauvegardées dans des fichiers et le transfert des pages anonymes froides vers la ZRAM, ce qui permet de maintenir la fluidité du système sans imposer de pression globale sur la mémoire.

Commandes shell

La commande am memory-limiter permet aux développeurs et aux intégrateurs système d'interagir avec le service au moment de l'exécution pour le développement, les tests et le débogage :

am memory-limiter <SUB-COMMAND>

état

La sous-commande status indique l'état opérationnel et les métriques actives du limiteur de mémoire :

adb shell am memory-limiter status

Voici un exemple de résultat :

Memory limiter
  enabled                  monitoring=true          ignored=none
  visibleMem=1948MB        visibleSwap=974MB
  notVisibleMem=974MB      notVisibleSwap=487MB
  started=36               watched=36               watch-failed=0
  events=0                 processes=36             process-hwm=36

Voici les principaux champs du résultat :

monitoring
Indique si le limiteur de mémoire surveille activement les processus.
visibleMem et notVisibleMem
Limites de mémoire absolues calculées (memory.high) actuellement appliquées pour chaque état de visibilité.
visibleSwap et notVisibleSwap
Limites de swap absolues calculées (memory.swap.max) actuellement appliquées pour chaque état de visibilité.
events
Nombre de fois où un processus a dépassé la limite qui lui a été attribuée.
processes
Nombre actuel de processus surveillés.

ignorer

La sous-commande ignore exclut temporairement un UID spécifique ou tous les processus de la limitation de mémoire. Cela est utile pour l'analyse comparative des performances, les tests de contrainte ou le diagnostic du comportement de la mémoire :

# Ignore a specific UID
adb shell am memory-limiter ignore 10087
# Ignore all processes (temporarily disables limiting)
adb shell am memory-limiter ignore all
# Resume normal limiting operation
adb shell am memory-limiter ignore none

manuel

La sous-commande manual remplace les limites calculées pour un processus spécifique par ID de processus (PID) par une valeur absolue personnalisée en octets. La valeur doit être un entier, mais peut inclure le suffixe MB pour indiquer des Mio ou le suffixe GB pour indiquer des Gio :

# Set a 1GiB limit for PID 1234
adb shell am memory-limiter manual 1234 1073741824
# Set a 1GiB limit for PID 1234
adb shell am memory-limiter manual 1234 1024MB
# Set a 1GiB limit for PID 1234
adb shell am memory-limiter manual 1234 1GB
# Remove the manual override for PID 1234
adb shell am memory-limiter manual 1234 none

Les remplacements manuels ne s'appliquent que pendant la durée de vie de cette instance de processus spécifique. Si le processus redémarre, il revient aux limites standards en fonction de son état.

Un forçage manuel ne peut pas dépasser les limites physiques du système.