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 en premier plan et 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 de 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 incontrô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

Le limiteur de mémoire applique les limites de la plate-forme indiquées sur cette page aux processus d'application (UID >= 10000). Les processus dont l'UID est inférieur à 10 000 ne sont pas soumis aux limites de la plate-forme, pas plus que le package du service d'inférence en bac à sable par défaut sur l'appareil (config_defaultOnDeviceSandboxedInferenceService). Les applications et services système peuvent toujours déclarer des budgets de mémoire plus stricts, comme décrit dans Budgets de mémoire système.

Le limiteur de mémoire 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). Étant donné que 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, des services d'arrière-plan ou des 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 les classifications de limite de mémoire de la plate-forme :

É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

Les processus PERSISTENT et PERSISTENT_UI ne reçoivent pas de limite de plate-forme. Si l'un de ces processus déclare un budget de mémoire, son budget de premier plan s'applique, comme décrit dans Buckets de budget de mémoire.

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.

Buckets de budget de mémoire

Sur les appareils compatibles avec les budgets de mémoire, les applications et les services système peuvent déclarer des budgets plus stricts que les limites de la plate-forme. Les applications déclarent des budgets dans leur fichier manifeste, comme décrit dans Définir des budgets de mémoire pour les applications. Pour les applications et services système, consultez Budgets de mémoire système. Un budget définit memory.high et ne peut pas dépasser la limite de la plate-forme pour le même état de processus.

Lorsqu'un processus dispose d'un budget, le limiteur de mémoire mappe l'état de son processus à l'un des quatre buckets. Les valeurs foreground, perceptible et background du fichier manifeste android:state sélectionnent le budget du bucket correspondant. Les processus du bucket mis en cache n'ont pas de budget actif.

État du processusTranche de budget
PERSISTENTPremier plan
PERSISTENT_UIPremier plan
TOPPremier plan
BOUND_TOPPremier plan
TOP_SLEEPINGPremier plan
FOREGROUND_SERVICEPerceptible
BOUND_FOREGROUND_SERVICEPerceptible
IMPORTANT_FOREGROUNDPerceptible
IMPORTANT_BACKGROUNDPerceptible
TRANSIENT_BACKGROUNDArrière-plan
BACKUPArrière-plan
SERVICEArrière-plan
RECEIVERArrière-plan
HEAVY_WEIGHTArrière-plan
HOMEArrière-plan
LAST_ACTIVITYArrière-plan
CACHED_ACTIVITYEn cache
CACHED_ACTIVITY_CLIENTEn cache
CACHED_RECENTEn cache
CACHED_EMPTYEn cache

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 allocations et les limites 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 les composants matériels spécialisés tels que le GPU, le modem de bande de base, l'ISP de l'appareil photo, les environnements d'exécution sécurisés et les hyperviseurs, qui consomment 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 garantit 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'interface utilisateur, de composition et de rendu 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. En dehors du service d'inférence en bac à sable par défaut sur l'appareil, le limiteur de mémoire n'exempte pas les applications individuelles, ce qui garantit une gouvernance é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, 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 leurs limites de mémoire 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 permutation absolues calculées (memory.swap.max) actuellement appliquées pour chaque état de visibilité.
events
Nombre de fois qu'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.