Android 17 y las versiones posteriores incluyen el Limitador de memoria, un servicio del sistema que supervisa y limita el uso de memoria de los procesos de la aplicación con cgroup v2 de Linux. El Limitador de memoria evita que las apps individuales consuman una cantidad desproporcionada de memoria del sistema, lo que reduce la presión general de la memoria y evita que se cierren de forma agresiva los procesos críticos en primer plano y almacenados en caché cuando hay poca memoria.
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 la jerarquía cgroup v2 del kernel de Linux.
Para admitir el Limitador de memoria, el kernel del dispositivo debe habilitar 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 página de forma proactiva de ese proceso.
memory.swap.max- Limita la cantidad máxima de espacio de intercambio (como ZRAM) que puede usar el proceso.
Impacto en las apps
El Limitador de memoria no afecta a las apps que operan dentro de sus límites de memoria.
Cuando una app supera su límite memory.high, el kernel expulsa la memoria limpia respaldada por archivos de la app y cambia su memoria anónima inactiva para mantener la app dentro del límite. Como resultado de esta expulsión de página y actividad de intercambio, la app podría experimentar una limitación temporal de la ejecución, pero continúa ejecutándose.
En situaciones extremas, como una fuga de memoria descontrolada en la que la app continúa asignando memoria anónima después de que se agota el espacio de intercambio, la app no puede asignar memoria y se finaliza.
Supervisión de procesos
De forma predeterminada, el Limitador de memoria supervisa los procesos de la app (UID >= 10000). Los procesos principales del sistema están exentos para preservar la estabilidad esencial del sistema.
El Limitador de memoria asigna límites de memoria según el estado de visibilidad del proceso:
Procesos visibles: Son procesos que alojan una IU visible para el usuario (como la actividad enfocada actualmente o una interacción activa). Debido a que mostrar una IU requiere un conjunto de trabajo más grande para la renderización y las canalizaciones de gráficos, los procesos visibles reciben un límite de memoria más generoso.
Procesos no visibles: Son procesos que realizan tareas en segundo plano sin presentar una IU (como servicios en segundo plano o receptores de transmisión). Debido a que no renderizan componentes de la IU, operan con un límite más restrictivo.
En la siguiente tabla, se asignan estados de proceso específicos a clasificaciones de límite de memoria:
| Estado del proceso | Límite de memoria |
|---|---|
PERSISTENT | Sin restricciones |
PERSISTENT_UI | Sin restricciones |
TOP | Visible |
BOUND_TOP | Visible |
FOREGROUND_SERVICE | No visible |
BOUND_FOREGROUND_SERVICE | No visible |
IMPORTANT_FOREGROUND | Visible |
IMPORTANT_BACKGROUND | No visible |
TRANSIENT_BACKGROUND | No visible |
BACKUP | No visible |
SERVICE | No visible |
RECEIVER | No visible |
TOP_SLEEPING | Visible |
HEAVY_WEIGHT | No visible |
HOME | No visible |
LAST_ACTIVITY | No visible |
CACHED_ACTIVITY | Almacenada en caché |
CACHED_ACTIVITY_CLIENT | Almacenada en caché |
CACHED_RECENT | Almacenada en caché |
CACHED_EMPTY | Almacenada en caché |
En el estado almacenado en caché, los procesos se congelan y, luego, se recuperan al máximo.
Cuando un proceso supera su límite memory.high asignado, el Limitador de memoria detecta el evento y puede activar acciones de diagnóstico, como capturar un perfil de memoria o registrar una anomalía en statsd.
Configuración
En Android 17 y versiones posteriores, el Limitador de memoria proporciona un archivo de configuración predeterminado de la plataforma estándar en la partición del sistema, mientras que admite anulaciones opcionales específicas del dispositivo en la partición vendor:
- Configuración predeterminada del sistema:
/system/etc/memory-limiter-config.xml - Anulación del proveedor (opcional):
/vendor/etc/memory-limiter-config.xml
El Limitador de memoria primero busca /vendor/etc/memory-limiter-config.xml.
Si está presente, usa la configuración del proveedor; de lo contrario, vuelve a la configuración predeterminada del sistema en /system/etc/memory-limiter-config.xml.
Formato XML
El archivo de configuración sigue el esquema definido en memory-limiter-config.xsd. El archivo define varios conjuntos de límites ordenados por memoria disponible; el servicio selecciona el conjunto de límites coincidentes más alto según la RAM disponible del dispositivo. Todos los valores de memoria se especifican en mebibytes (MiB).
<MemoryLimiterConfig>
<version>1</version>
<configList>
<!-- RAM minimums account for MemTotal excluding carve-outs. -->
<limitSet>
<!-- Limits for 16GB RAM device (MemTotal >= 13GiB): 10G/5G/5G/5G -->
<minimumRequiredMemTotal>13312</minimumRequiredMemTotal>
<memVisible>10240</memVisible>
<memNotVisible>5120</memNotVisible>
<swapVisible>5120</swapVisible>
<swapNotVisible>5120</swapNotVisible>
</limitSet>
<limitSet>
<!-- Limits for 12GB RAM device (MemTotal >= 10GiB): 8G/4G/4G/4G -->
<minimumRequiredMemTotal>10240</minimumRequiredMemTotal>
<memVisible>8192</memVisible>
<memNotVisible>4096</memNotVisible>
<swapVisible>4096</swapVisible>
<swapNotVisible>4096</swapNotVisible>
</limitSet>
<limitSet>
<!-- Limits for 8GB RAM device (MemTotal >= 6.5GiB): 5G/3G/3G/3G -->
<minimumRequiredMemTotal>6656</minimumRequiredMemTotal>
<memVisible>5120</memVisible>
<memNotVisible>3072</memNotVisible>
<swapVisible>3072</swapVisible>
<swapNotVisible>3072</swapNotVisible>
</limitSet>
<limitSet>
<!-- Limits for 6GB RAM device (MemTotal >= 4.5GiB): 4G/2G/2G/2G -->
<minimumRequiredMemTotal>4608</minimumRequiredMemTotal>
<memVisible>4096</memVisible>
<memNotVisible>2048</memNotVisible>
<swapVisible>2048</swapVisible>
<swapNotVisible>2048</swapNotVisible>
</limitSet>
<limitSet>
<!-- Limits for 4GB RAM device (MemTotal >= 3GiB): 2G/1G/1G/1G -->
<minimumRequiredMemTotal>3072</minimumRequiredMemTotal>
<memVisible>2048</memVisible>
<memNotVisible>1024</memNotVisible>
<swapVisible>1024</swapVisible>
<swapNotVisible>1024</swapNotVisible>
</limitSet>
</configList>
</MemoryLimiterConfig>
version- Es un número entero positivo que identifica la versión de configuración. Debe ser
1. minimumRequiredMemTotalEs el
MemTotalmínimo del sistema (en MiB) que se requiere para que se aplique este conjunto de límites. El servicio evalúa esto en función de la memoria disponible total que informa el kernel en/proc/meminfo.Información sobre las asignaciones de memoria: El valor
MemTotaldel kernel refleja la DRAM física a la que puede acceder el sistema operativo después de que se deducen las asignaciones de memoria de hardware y firmware. Las asignaciones reservan RAM para componentes de hardware especializados, como la GPU, el módem de banda base, el ISP de la cámara, los entornos de ejecución seguros y los hipervisores, que suelen consumir entre 500 MiB y más de 1 GiB de memoria física.Debido a que
MemTotales más bajo que la DRAM física anunciada del dispositivo,minimumRequiredMemTotallos umbrales incluyen un margen para estas asignaciones. Por ejemplo, una configuración orientada a dispositivos de 16 GB especifica unminimumRequiredMemTotalde13312(13 GiB) en lugar de16384(16 GiB), y un nivel para dispositivos de 12 GB especifica10240(10 GiB). Esto garantiza que cada dispositivo coincida con el nivel previsto, independientemente de las variaciones de asignación a nivel de la placa.memVisibleEs el límite flexible de memoria (
memory.high) en MiB que se aplica a los procesos visibles.memNotVisibleEs el límite flexible de memoria (
memory.high) en MiB que se aplica a los procesos no visibles.swapVisibleEs el límite de espacio de intercambio (
memory.swap.max) en MiB que se aplica a los procesos visibles.swapNotVisibleEs el límite de espacio de intercambio (
memory.swap.max) en MiB que se aplica a los procesos no visibles.
Límites estándar de la plataforma
En Android 17 y versiones posteriores, la plataforma especifica límites estándar de memoria y de intercambio en /system/etc/memory-limiter-config.xml en los niveles comunes de RAM física:
| RAM física de destino |
MemTotal requerida mínima (minimumRequiredMemTotal) |
Límite de memoria visible (memVisible) |
Límite de memoria no visible (memNotVisible) |
Límite de intercambio visible (swapVisible) |
Límite de intercambio no visible (swapNotVisible) |
|---|---|---|---|---|---|
| 16 GB | 13,312 MiB (13 GiB) | 10,240 MiB (10 GiB) | 5,120 MiB (5 GiB) | 5,120 MiB (5 GiB) | 5,120 MiB (5 GiB) |
| 12 GB | 10,240 MiB (10 GiB) | 8,192 MiB (8 GiB) | 4,096 MiB (4 GiB) | 4,096 MiB (4 GiB) | 4,096 MiB (4 GiB) |
| 8 GB | 6,656 MiB (6.5 GiB) | 5,120 MiB (5 GiB) | 3,072 MiB (3 GiB) | 3,072 MiB (3 GiB) | 3,072 MiB (3 GiB) |
| 6 GB | 4,608 MiB (4.5 GiB) | 4,096 MiB (4 GiB) | 2,048 MiB (2 GiB) | 2,048 MiB (2 GiB) | 2,048 MiB (2 GiB) |
| 4 GB | 3,072 MiB (3 GiB) | 2,048 MiB (2 GiB) | 1,024 MiB (1 GiB) | 1,024 MiB (1 GiB) | 1,024 MiB (1 GiB) |
Principios del límite de memoria
La configuración del Limitador de memoria se diseñó en función de los siguientes principios de la plataforma:
Coherencia del ecosistema y compatibilidad de apps: Los límites de memoria coherentes en todos los dispositivos garantizan un rendimiento predecible de las apps en todo el ecosistema de Android. Las apps se desarrollan y prueban en función de las expectativas de memoria estándar; mantener límites uniformes de la plataforma evita la limitación inesperada o las finalizaciones prematuras.
Asignación proporcional de recursos: Los límites de la plataforma se calibran según la capacidad de RAM física:
- Procesos visibles: Se asigna aproximadamente de 1/2 a 2/3 de la RAM física total para admitir cargas de trabajo activas de IU, composición y renderización.
- Procesos no visibles: Se asigna aproximadamente de 1/4 a 1/3 de la RAM física total para tareas en segundo plano.
Aplicabilidad universal: Los límites se aplican de manera uniforme a todos los procesos de la aplicación en el dispositivo (UID >= 10000), incluidas las apps preinstaladas del sistema y del OEM. El Limitador de memoria no admite listas de entidades permitidas para eximir apps específicas, lo que garantiza una administración justa de la memoria en todas las apps.
Ausencia de APIs de consulta en tiempo de ejecución: En Android 17 y versiones posteriores, las apps no pueden consultar de forma programática sus límites de memoria asignados en tiempo de ejecución. Los límites estándar de la plataforma se definen con márgenes generosos para que las apps que se comportan bien operen sin restricciones durante el uso normal.
Recuperación del kernel y dinámica de intercambio: Cuando un proceso de la app se acerca a su límite
memory.high, el kernel de Linux dirige la recuperación de memoria específicamente a ese cgroup de proceso. Esto incluye descartar páginas inactivas respaldadas por archivos y cambiar páginas anónimas inactivas a ZRAM, lo que mantiene la fluidez del sistema sin imponer presión de memoria global.
Comandos del shell
El comando am memory-limiter permite que los desarrolladores y los integradores de sistemas interactúen con el servicio en tiempo de ejecución para el desarrollo, las pruebas y la depuración:
am memory-limiter <SUB-COMMAND>estado
El subcomando status informa el estado operativo y las métricas activas del Limitador de memoria:
adb shell am memory-limiter statusResultado 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
Entre los campos clave del resultado, se incluyen los siguientes:
monitoring- Indica si el Limitador de memoria está observando activamente los procesos.
visibleMemynotVisibleMem- Son los límites de memoria absolutos calculados (
memory.high) que se aplican actualmente para cada estado de visibilidad. visibleSwapynotVisibleSwap- Son los límites de intercambio absolutos calculados (
memory.swap.max) que se aplican actualmente para cada estado de visibilidad. events- Es la cantidad de veces que un proceso superó su límite asignado.
processes- Es la cantidad actual de procesos supervisados.
ignore
El subcomando ignore excluye temporalmente un UID específico o todos los procesos de la limitación de memoria. Esto es útil para la evaluación comparativa del rendimiento, las pruebas de esfuerzo o el diagnóstico del comportamiento de la memoria:
# 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
manual
El subcomando manual anula los límites calculados para un proceso específico por ID de proceso (PID) con un valor absoluto personalizado en bytes. El valor debe ser un número entero, pero puede incluir el sufijo MB para indicar MiB o el sufijo GB para indicar GiB:
# 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
Las anulaciones manuales solo se aplican durante la vida útil de esa instancia de proceso específica. Si se reinicia el proceso, vuelve a los límites estándar según su estado.
Una anulación manual no puede superar los límites físicos del sistema.