Daemon de optimización de poca memoria

El proceso del daemon de optimización de poca memoria de Android (lmkd) supervisa el estado de la memoria de un sistema Android en ejecución y reacciona a la alta presión de memoria cerrando los procesos menos esenciales para mantener el rendimiento del sistema en niveles aceptables.

Acerca de la presión de la memoria

Un sistema Android que ejecuta varios procesos en paralelo puede encontrarse con situaciones en las que se agota la memoria del sistema y los procesos que requieren más memoria experimentan demoras notables. La presión de memoria, un estado en el que el sistema se está quedando sin memoria, requiere que Android libere memoria (para aliviar la presión) limitando o finalizando procesos no importantes, solicitando a los procesos que liberen recursos almacenados en caché no críticos, etcétera.

Históricamente, Android supervisó la presión de la memoria del sistema con un controlador de eliminación de memoria baja (LMK) en el kernel, un mecanismo rígido que depende de valores codificados. A partir del kernel 4.12, el controlador LMK se quitó del kernel upstream y el lmkd del espacio del usuario realiza tareas de supervisión de la memoria y finalización de procesos.

Información sobre la detención por presión

Android 10 y versiones posteriores admiten un nuevo modo lmkd que usa monitores de información de demora de presión (PSI) del kernel para la detección de presión de la memoria. El conjunto de parches de PSI en el kernel upstream (portado a los kernels 4.9 y 4.14) mide la cantidad de tiempo que se retrasan las tareas como resultado de la escasez de memoria. Como estas demoras afectan directamente la experiencia del usuario, representan una métrica conveniente para determinar la gravedad de la presión de la memoria. El kernel upstream también incluye monitores de PSI que permiten que los procesos privilegiados del espacio del usuario (como lmkd) especifiquen umbrales para estas demoras y se suscriban a eventos del kernel cuando se supera un umbral.

Monitores de PSI en comparación con los indicadores de vmpressure

Dado que los indicadores de vmpressure (generados por el kernel para la detección de presión de memoria y utilizados por lmkd) suelen incluir numerosos falsos positivos, lmkd debe realizar un filtrado para determinar si la memoria está bajo presión real. Esto genera activaciones lmkd innecesarias y el uso de recursos computacionales adicionales. El uso de PSI supervisa los resultados y permite detectar con mayor precisión la presión de la memoria, además de minimizar la sobrecarga del filtrado.

Usa monitores de PSI

Para usar los monitores de PSI en lugar de los eventos vmpressure, configura la propiedad ro.lmk.use_psi. El valor predeterminado es true, lo que convierte a los monitores de PSI en el mecanismo predeterminado de detección de presión de la memoria para lmkd. Dado que los monitores de PSI requieren compatibilidad con el kernel, este debe incluir los parches de portabilidad a versiones anteriores de PSI y compilarse con la compatibilidad con PSI habilitada (CONFIG_PSI=y).

Desventajas del controlador LMK en el kernel

Android dejó de utilizar el controlador LMK debido a varios problemas, entre los que se incluyen los siguientes:

  • Los dispositivos con poca RAM debían ajustarse de forma agresiva y, aun así, tenían un rendimiento deficiente en cargas de trabajo con una gran caché de páginas activa respaldada por archivos. El bajo rendimiento provocó un thrashing y no se registraron muertes.
  • El controlador del kernel de LMK se basaba en límites de memoria libre, sin escalamiento según la presión de la memoria.
  • Debido a la rigidez del diseño, los socios a menudo personalizaban el controlador para que funcionara en sus dispositivos.
  • El controlador de LMK se conectó a la API de reducción de losas, que no se diseñó para operaciones pesadas, como la búsqueda de destinos y la finalización de procesos, lo que ralentizó el proceso de vmscan.

LMKD de espacio del usuario

El lmkd del espacio del usuario implementa la misma funcionalidad que el controlador en el kernel, pero usa mecanismos existentes del kernel para detectar y estimar la presión de la memoria. Estos mecanismos incluyen el uso de eventos vmpressure generados por el kernel o supervisores de información de demora de presión (PSI) para recibir notificaciones sobre los niveles de presión de la memoria, y el uso de funciones de cgroup de memoria para limitar los recursos de memoria asignados a cada proceso según su importancia.

Cómo usar lmkd de espacio del usuario en Android 10

En Android 9 y versiones posteriores, el lmkd del espacio del usuario se activa si no se detecta un controlador de LMK en el kernel. Dado que lmkd del espacio de usuario requiere compatibilidad del kernel con los cgroups de memoria, el kernel debe compilarse con los siguientes parámetros de configuración:

CONFIG_ANDROID_LOW_MEMORY_KILLER=n
CONFIG_MEMCG=y
CONFIG_MEMCG_SWAP=y

Estrategias de eliminación

El espacio del usuario lmkd admite estrategias de eliminación basadas en eventos vmpressure o monitores de PSI, su gravedad y otras sugerencias, como la utilización del intercambio. Las estrategias de eliminación difieren entre los dispositivos de memoria insuficiente y los de alto rendimiento:

  • En los dispositivos con poca memoria, el sistema debe tolerar una mayor presión de memoria como un modo de funcionamiento normal.
  • En los dispositivos de alto rendimiento, la presión de memoria debe considerarse una situación anormal y corregirse antes de que afecte el rendimiento general.

Puedes configurar la estrategia de eliminación con la propiedad ro.config.low_ram.

El lmkd del espacio del usuario también admite un modo heredado en el que toma decisiones de eliminación con las mismas estrategias que el controlador LMK en el kernel (es decir, umbrales de memoria libre y caché de archivos). Para habilitar el modo heredado, establece la propiedad ro.lmk.use_minfree_levels en true.

Cómo configurar lmkd

Configura lmkd para un dispositivo específico con las siguientes propiedades.

Propiedad Usar Predeterminado
ro.config.low_ram Especifica si el dispositivo es de bajo rendimiento o de alto rendimiento. false
ro.lmk.use_psi Usa monitores de PSI (en lugar de eventos vmpressure). true
ro.lmk.use_minfree_levels Usa umbrales de memoria libre y caché de archivos para tomar decisiones sobre la finalización de procesos (es decir, coincide con la funcionalidad del controlador LMK en el kernel). false
ro.lmk.low Es la puntuación mínima de oom_adj para los procesos aptos para finalizarse en un nivel bajo de vmpressure. 1001
(inhabilitado)
ro.lmk.medium Es la puntuación mínima de oom_adj para los procesos aptos para finalizarse en el nivel medio de vmpressure. 800
(servicios almacenados en caché o no esenciales)
ro.lmk.critical Es la puntuación mínima de oom_adj para los procesos aptos para finalizarse en el nivel crítico de vmpressure. 0
(cualquier proceso)
ro.lmk.critical_upgrade Habilita la actualización al nivel crítico. false
ro.lmk.upgrade_pressure Es el valor máximo de mem_pressure en el que se actualiza el nivel porque el sistema realiza demasiados intercambios. 100
(inhabilitado)
ro.lmk.downgrade_pressure Es el valor mínimo de mem_pressure en el que se ignora un evento de vmpressure porque aún hay suficiente memoria libre disponible. 100
(inhabilitado)
ro.lmk.kill_heaviest_task Elimina la tarea apta más pesada (mejor decisión) en lugar de cualquier tarea apta (decisión rápida). false
ro.lmk.kill_timeout_ms Duración en milisegundos después de una eliminación cuando no se realizará ninguna eliminación adicional. 0
(inhabilitado)
ro.lmk.debug Habilita los registros de depuración de lmkd. false

Ejemplo de configuración del dispositivo:

PRODUCT_PROPERTY_OVERRIDES += \
    ro.lmk.low=1001 \
    ro.lmk.medium=800 \
    ro.lmk.critical=0 \
    ro.lmk.critical_upgrade=false \
    ro.lmk.upgrade_pressure=100 \
    ro.lmk.downgrade_pressure=100 \
    ro.lmk.kill_heaviest_task=true

LMKD de espacio del usuario en Android 11

Android 11 mejora el lmkd con la introducción de una nueva estrategia de cierre. La estrategia de eliminación usa un mecanismo de PSI para la detección de presión de memoria que se introdujo en Android 10. lmkd en Android 11 tiene en cuenta los niveles de uso de recursos de memoria y el thrashing para evitar la falta de memoria y la degradación del rendimiento. Esta estrategia de cierre reemplaza las anteriores y se puede usar en dispositivos de alto rendimiento y con poca RAM (Android Go).

Requisitos del kernel

En el caso de los dispositivos con Android 11, lmkd requiere las siguientes funciones del kernel:

  • Incluye parches de PSI y habilita PSI (hay versiones disponibles en los kernels comunes de Android 4.9, 4.14 y 4.19).
  • Incluye parches de compatibilidad con PIDFD (hay versiones anteriores disponibles en los kernels comunes de Android 4.9, 4.14 y 4.19).
  • En el caso de los dispositivos con poca RAM, incluye cgroups de memoria.

El kernel debe compilarse con los siguientes parámetros de configuración:

CONFIG_PSI=y

Cómo configurar lmkd en Android 11

La estrategia de cierre de memoria en Android 11 admite los parámetros de ajuste y los valores predeterminados que se indican a continuación. Estas funciones están disponibles en dispositivos de alto rendimiento y con poca RAM.

Propiedad Usar Predeterminado
Alto rendimiento Poca RAM
ro.lmk.psi_partial_stall_ms Es el umbral de detención de PSI parcial, en milisegundos, para activar la notificación de memoria insuficiente. Si el dispositivo recibe notificaciones de presión en la memoria demasiado tarde, disminuye este valor para activar notificaciones antes. Si las notificaciones de presión en la memoria se activan innecesariamente, aumenta este valor para que el dispositivo sea menos sensible al ruido. 70 200
ro.lmk.psi_complete_stall_ms Es el umbral completo de detención de PSI, en milisegundos, para activar notificaciones de memoria críticas. Si el dispositivo recibe notificaciones de presión de memoria crítica demasiado tarde, disminuye este valor para activar notificaciones antes. Si las notificaciones de presión de memoria crítica se activan innecesariamente, aumenta este valor para que el dispositivo sea menos sensible al ruido. 700
ro.lmk.thrashing_limit Cantidad máxima de errores de referencia del conjunto de trabajo como porcentaje del tamaño total de la caché de páginas respaldada por archivos. Los refaltos del conjunto de trabajo por encima de este valor significan que se considera que el sistema está generando una sobrecarga en su caché de páginas. Si el rendimiento del dispositivo se ve afectado durante la presión de la memoria, disminuye el valor para limitar el thrashing. Si el rendimiento del dispositivo se reduce innecesariamente por motivos de saturación, aumenta el valor para permitir más saturación. 100 30
ro.lmk.thrashing_limit_decay Es la disminución del umbral de pérdida de rendimiento expresada como un porcentaje del umbral original que se usa para reducir el umbral cuando el sistema no se recupera, incluso después de una eliminación. Si el descarte continuo produce eliminaciones innecesarias, disminuye el valor. Si la respuesta a la pérdida continua después de una eliminación es demasiado lenta, aumenta el valor. 10 50
ro.lmk.swap_util_max Es la cantidad máxima de memoria intercambiada como porcentaje de la memoria total intercambiable. Cuando la memoria intercambiada supera este límite, significa que el sistema intercambió la mayor parte de su memoria intercambiable y aún está bajo presión. Esto puede ocurrir cuando las asignaciones no intercambiables generan presión de memoria que no se puede aliviar con el intercambio, ya que la mayor parte de la memoria intercambiable ya se intercambió. El valor predeterminado es 100, lo que inhabilita esta verificación. Si el rendimiento del dispositivo se ve afectado durante la presión en la memoria mientras la utilización del intercambio es alta y el nivel de intercambio libre no disminuye a ro.lmk.swap_free_low_percentage, disminuye el valor para limitar la utilización del intercambio. 100 100

Las siguientes variables de ajuste antiguas también funcionan con la nueva estrategia de eliminación.

Propiedad Usar Predeterminado
Alto rendimiento Poca RAM
ro.lmk.swap_free_low_percentage Es el nivel de intercambio libre como porcentaje del espacio de intercambio total. `lmkd` usa este valor como umbral para determinar cuándo el sistema tiene poco espacio de intercambio. Si `lmkd` finaliza procesos cuando hay demasiado espacio en el intercambio, disminuye el porcentaje. Si las finalizaciones de `lmkd` ocurren demasiado tarde y permiten que se produzcan finalizaciones por OOM, aumenta el porcentaje. 20 10
ro.lmk.debug Esto habilita los registros de depuración de `lmkd`. Habilita la depuración durante el ajuste. false