Limiteur de mémoire

Android 17 et versions ultérieures incluent le Memory Limiter, 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 Memory Limiter 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 empêche les suppressions agressives de mémoire faible des processus critiques au premier plan et mis en cache.

Mécanisme

Le Memory Limiter 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 Memory Limiter applique les limites de mémoire à l'aide de la hiérarchie Linux kernel cgroup v2.

Pour prendre en charge le Memory Limiter, le noyau de l'appareil doit activer cgroup v2 et le contrôleur memory. Le service repose spécifiquement sur les attributs suivants :

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

Impact sur les applications

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

Lorsqu'une application dépasse sa limite memory.high, le noyau supprime la mémoire propre de l'application et échange sa mémoire anonyme inactive pour que l'application reste dans la limite. En raison de cette suppression de page et de cette activité d'échange, l'application peut subir une limitation temporaire de l'exécution, mais elle continue de s'exécuter.

Dans des situations extrêmes, telles qu'une fuite de mémoire incontrôlée où l'application continue d'allouer de la mémoire anonyme une fois l'espace d'échange épuisé, l'application ne parvient pas à allouer de la mémoire et est arrêtée.

Surveillance des processus

Le Memory Limiter surveille les processus d'application (UID >= 10000) par défaut. Les processus système principaux sont exemptés afin de préserver la stabilité essentielle 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 interface utilisateur visible par l'utilisateur (telle que l'activité actuellement ciblée ou une interaction active). Étant donné que l'affichage d'une interface utilisateur 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'interface utilisateur (tels que les services en arrière-plan ou les récepteurs de diffusion). Comme ils ne rendent pas les composants de l'interface utilisateur, ils fonctionnent avec une limite plus restrictive.

Le tableau suivant mappe des états de processus spécifiques à 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 sa limite memory.high attribuée, le Memory Limiter 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 Memory Limiter est configuré à l'aide du fichier de configuration de plate-forme standard sur la partition système à l'adresse /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 configuration. Doit être 1.
minimumRequiredMemTotal

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

Comprendre les limites et les limites de mémoire : la valeur MemTotal du noyau reflète la DRAM physique accessible au système d'exploitation après déduction des limites de mémoire matérielles et micrologicielles. Les limites réservent de la RAM pour des composants matériels spécialisés tels que le GPU, le modem de bande de base, l'ISP de la caméra, 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, minimumRequiredMemTotal seuils incluent une marge pour ces limites. Par exemple, une configuration ciblant les appareils de 16 Go spécifie un plancher de 14336 (14 Gio) au lieu de 16384 (16 Gio), et un niveau pour les appareils de 12 Go spécifie un plancher de 9216 (9 Gio). Cela garantit que chaque appareil correspond au niveau prévu, quelles que soient les variations de limites au niveau de la carte.

maximumAllowedMemTotal

Système MemTotal maximal (en Mio, plafond exclusif) pour que cet ensemble de limites s'applique. Si MemTotal est supérieur ou égal à ce plafond, l'ensemble de limites ne s'applique pas. Pour les appareils avec 18 432 Mio ou plus, ou pour les appareils avec moins de 3 200 Mio, le Memory Limiter n'applique pas de limites, ce qui laisse les processus sans contrainte.

memVisible

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

memNotVisible

Limite de mémoire flexible (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 de plate-forme standards

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 sur 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 de limite de mémoire

La configuration du Memory Limiter 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'ensemble de l'écosystème Android. Les applications sont développées et testées par rapport aux attentes de mémoire standards. 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 la RAM physique :

    • Processus visibles : environ 1/2 à 2/3 de la RAM physique totale allouée pour prendre en charge les charges de travail d'interface utilisateur, de composition et de rendu actives.
    • Processus non visibles : environ 1/4 à 1/3 de la RAM physique totale allouée pour les 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 Memory Limiter ne prend pas en charge les listes d'autorisation pour exempter des applications spécifiques, ce qui garantit une gestion équitable de la mémoire dans toutes les applications.

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

  • Récupération du noyau et dynamique d'échange : 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 sauvegardées inactives et l'échange des pages anonymes froides vers ZRAM, ce qui maintient 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 Memory Limiter :

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

Les champs clés du résultat incluent les suivants :

monitoring
Indique si le Memory Limiter 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 d'échange absolues calculées (memory.swap.max) actuellement appliquées pour chaque état de visibilité.
events
Nombre de fois qu'un processus a dépassé sa limite 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 résistance 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 un ID de processus (PID) avec une valeur absolue personnalisée en octets. La valeur doit être un entier, mais peut inclure le suffixe MB pour indiquer Mio ou le suffixe GB pour indiquer 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 remplacement manuel ne peut pas dépasser les limites physiques du système.