Limitador de memoria

Android 17 y versiones posteriores admiten el limitador de memoria, que es un servicio del sistema que supervisa y limita el uso de memoria de los procesos de aplicaciones con cgroup v2 de Linux. El limitador de memoria evita que las apps individuales consuman demasiada memoria del sistema, lo que reduce la presión de la memoria en todo el sistema y evita la finalización agresiva por memoria insuficiente (OOM) de procesos críticos.

Mecanismos

El limitador de memoria se integra con el servicio de Activity Manager (AMS) para hacer un seguimiento de los eventos del ciclo de vida del proceso y los cambios de estado. El limitador de memoria aplica límites de memoria con el sistema de archivos cgroup v2 del kernel de Linux.

Para usar el limitador de memoria, el kernel del dispositivo debe admitir cgroup v2 y el controlador memory. Específicamente, el servicio se basa en los siguientes atributos:

memory.high
Es un límite flexible. Cuando se supera, se limita el proceso y el kernel intenta recuperar memoria de él.
memory.swap.max
Limita la cantidad de espacio de intercambio que puede usar el proceso.

Impacto en las apps

El Limitador de memoria no afecta a las apps que no superan sus límites de memoria.

Cuando una app supera su límite de memory.high, el kernel expulsa la memoria respaldada por archivos de la app y permuta su memoria anónima para mantener la app dentro del límite. Como resultado del desalojo y el intercambio, es posible que la app se ejecute más lento.

En el peor de los casos, si la app sigue asignando memoria anónima y el dispositivo se queda sin espacio de intercambio, es posible que la app no pueda asignar memoria y, como resultado, es probable que falle.

Supervisión de procesos

De forma predeterminada, el Limitador de memoria supervisa los procesos de las apps (UID >= 10000). Por lo general, los procesos del sistema están exentos para ayudar a verificar la estabilidad del sistema principal.

El limitador de memoria asigna límites de memoria según el estado del proceso:

  • Los procesos visibles son perceptibles para el usuario, como las actividades en primer plano, los servicios en primer plano o cualquier otro estado perceptible de jank.

  • Los procesos no visibles son procesos en segundo plano que no interactúan con el usuario ni son visibles para él.

En la siguiente tabla, se asignan estados de procesos específicos a límites de memoria:

Estado del procesoLímite de memoria
PERSISTENTSin restricciones
PERSISTENT_UISin restricciones
TOPVisible
BOUND_TOPVisible
FOREGROUND_SERVICENo visible
BOUND_FOREGROUND_SERVICENo visible
IMPORTANT_FOREGROUNDVisible
IMPORTANT_BACKGROUNDNo visible
TRANSIENT_BACKGROUNDNo visible
BACKUPNo visible
SERVICENo visible
RECEIVERNo visible
TOP_SLEEPINGVisible
HEAVY_WEIGHTNo visible
HOMENo visible
LAST_ACTIVITYNo visible
CACHED_ACTIVITYAlmacenado en caché
CACHED_ACTIVITY_CLIENTAlmacenado en caché
CACHED_RECENTAlmacenado en caché
CACHED_EMPTYAlmacenado en caché

En el estado almacenado en caché, los procesos se congelan y, luego, se recuperan al máximo.

Cuando un proceso supera el límite de memory.high asignado, el limitador de memoria detecta el evento y puede activar acciones de depuración, como capturar un perfil de memoria o registrar una anomalía en statsd.

Configuración

Configura el limitador de memoria con un archivo en formato XML ubicado en la partición vendor. La configuración te permite ajustar los límites de memoria absolutos según las restricciones de memoria específicas del dispositivo.

  • Ruta de acceso al archivo: /vendor/etc/memory-limiter-config.xml

  • Configuración predeterminada: Si no se encuentra el archivo de configuración o si no se puede leer o no es válido, se inhabilita el limitador de memoria.

Formato XML

El archivo de configuración sigue el esquema definido en memory-limiter-config.xsd. El archivo te permite definir varios conjuntos de límites. El servicio elige la mejor coincidencia según la RAM disponible del dispositivo. Todos los valores de memoria se definen en unidades de mebibytes (MiB).

<MemoryLimiterConfig>
  <version>1</version>
  <configList>
    <limitSet>
      <!-- Limits for a phone with at least 14G of ram: 8G/4G/4G/4G -->
      <minimumRequiredMemTotal>14336</minimumRequiredMemTotal>
      <memVisible>8192</memVisible>
      <memNotVisible>4096</memNotVisible>
      <swapVisible>4096</swapVisible>
      <swapNotVisible>4096</swapNotVisible>
    </limitSet>
  </configList>
</MemoryLimiterConfig>
version
Es un número entero positivo que identifica la versión de la configuración. Debe ser 1.
minimumRequiredMemTotal
Es la memoria del sistema disponible mínima requerida para que este límite sea válido.
memVisible
Límite de memoria (memory.high) permitido para los procesos visibles.
memNotVisible
Límite de memoria (memory.high) permitido para los procesos no visibles.
swapVisible
Límite de intercambio (memory.swap.max) permitido para los procesos visibles.
swapNotVisible
Es el límite de intercambio (memory.swap.max) permitido para los procesos no visibles.

Lineamientos sobre el límite de memoria del dispositivo

Cuando configures los límites de memoria para tu dispositivo, ten en cuenta los siguientes lineamientos:

  • Límites personalizados según las capacidades del hardware: Los OEM de dispositivos pueden establecer límites personalizados según las capacidades del hardware de sus dispositivos. Android recomienda los siguientes rangos:

    • Procesos visibles: Al menos 1/2 y, como máximo, 2/3 de la RAM física total.
    • Procesos no visibles: De 1/4 a 1/3 de la RAM física total Los OEM pueden tomar diferentes determinaciones según las capacidades y los casos de uso del dispositivo.
  • No hay API de tiempo de ejecución para las apps: A partir de Android 17 (SDK 37), las apps no tienen una API para consultar los límites de memoria en el tiempo de ejecución. Los OEM deben tener esto en cuenta y evitar establecer límites demasiado bajos, lo que garantiza que las apps no alcancen los límites durante los casos de uso razonables.

  • Configuración universal: Los límites se aplican a todos los procesos de la app en el dispositivo, incluidas las apps preinstaladas. No hay una lista de entidades permitidas para eximir a ciertas apps de estos límites.

Modificar la configuración

Para cambiar los límites en todo el sistema, sigue estos pasos:

  1. Modifica /vendor/etc/memory-limiter-config.xml.
  2. Reinicia el dispositivo o system_server para que se apliquen los cambios.

Comandos del shell

El comando am memory-limiter te permite interactuar con el servicio en tiempo de ejecución para el desarrollo y las pruebas:

am memory-limiter <SUB-COMMAND>

estado

El subcomando status informa el estado operativo del Limitador de memoria:

adb shell am memory-limiter status

Resultado de ejemplo:

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

Los campos clave en el resultado incluyen los siguientes:

monitoring
Indica si el limitador está supervisando activamente los procesos.
visibleMem y notVisibleMem
Indica los límites de memoria absolutos calculados para cada estado.
events
Cantidad de veces que un proceso superó su límite.
processes
Es la cantidad de procesos supervisados.

Ignorar

El subcomando ignore excluye temporalmente un UID o todos los procesos de la limitación. Esta acción es útil para las pruebas de rendimiento o cuando se permite que una app específica supere sus límites.

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

manual

El subcomando manual anula los límites calculados para un proceso específico (por ID de proceso o PID) con un valor absoluto personalizado en megabytes (MB):

adb shell am memory-limiter manual 1234 1024   // Set a 1024 MB limit for PID 1234
adb shell am memory-limiter manual 1234 none // Remove the manual override for PID 1234

Las anulaciones manuales solo se aplican al ciclo de vida del proceso. Si se reinicia el proceso, se restablecen los límites predeterminados según su estado.