Limitador de memoria

Android 17 y las 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 la aplicación con Linux cgroup v2. 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 falta de memoria (OOM) de los procesos críticos.

Mecanismos

El limitador de memoria se integra con el servicio de administrador de actividades (AMS) para hacer un seguimiento de los eventos del ciclo de vida del proceso y los cambios de estado. El limitador de memoria aplica los 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. El servicio se basa específicamente en los siguientes atributos:

memory.high
Es un límite flexible. Cuando se supera, se limita el proceso y el kernel intenta recuperar la memoria.
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 cambia su memoria anónima para mantener la app dentro del límite. Como resultado de la expulsión y el intercambio, es posible que la app se ejecute más lento.

En el extremo, si la app continúa 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 la app (UID >= 10000). Por lo general, los procesos del sistema están exentos para ayudar a verificar la estabilidad del sistema central.

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 proceso 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 en función de 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 configuración. Debe ser 1.
minimumRequiredMemTotal
Es la memoria mínima del sistema disponible requerida para que este conjunto de límites sea válido.
memVisible
Es el límite de memoria (memory.high) permitido para los procesos visibles.
memNotVisible
Es el límite de memoria (memory.high) permitido para los procesos no visibles.
swapVisible
Es el 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.

Modifica 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 los cambios tengan efecto.

Comandos del shell

El comando am memory-limiter te permite a ti y a los desarrolladores 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 lo siguiente:

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

ignore

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 el proceso se reinicia, vuelve a los límites predeterminados según su estado.