La optimización de la memoria es fundamental para ofrecer experiencias de juego estables y de alto rendimiento en Android. En esta guía, se proporciona una descripción general de por qué es importante la eficiencia de la memoria, cómo el sistema operativo Android administra los límites de memoria de los procesos y las nuevas métricas de memoria en Google Play Console para ayudarte a supervisar y mejorar la calidad técnica de tu juego.
La importancia de la optimización de la memoria
Optimizar la memoria de tu juego es fundamental para mantener la retención de jugadores, expandir la compatibilidad con dispositivos y cumplir con los estándares de calidad de la plataforma:
- Prevención de inicios en frío (experiencia y retención del usuario): Cuando un jugador cambia temporalmente de juego (por ejemplo, para responder una notificación o revisar un mensaje), el sistema operativo coloca el proceso del juego en segundo plano. Si el espacio en memoria en segundo plano del juego es demasiado alto, el optimizador de poca memoria (LMK) del sistema prioriza la finalización del proceso del juego para recuperar RAM para las tareas en primer plano. La próxima vez que el usuario reanude el juego, en lugar de una reanudación en caliente fluida e instantánea, el juego deberá pasar por un inicio en frío largo, en el que se vuelvan a cargar por completo los recursos gráficos pesados, el audio y los objetos binarios del motor de juego desde el almacenamiento. Mantener bajo el uso de memoria en segundo plano evita estas finalizaciones silenciosas, lo que preserva el estado del usuario y garantiza que los jugadores puedan reanudar su sesión de inmediato. Para obtener más detalles sobre el comportamiento del LMK del sistema, consulta la guía Android vitals: Eliminadores de memoria insuficiente.
- Estabilidad del ecosistema y del dispositivo: El uso ineficiente de la memoria y las fugas de memoria degradan el estado general del sistema. Cuando la memoria del sistema es escasa, el sistema se enfrenta a una presión severa, lo que genera caídas en la velocidad de fotogramas, interrupciones en la IU y fallas de audio. Si la presión de memoria es demasiado alta, el eliminador de memoria insuficiente (LMK) del sistema finaliza de forma agresiva los procesos en segundo plano, lo que obliga a otras aplicaciones a experimentar inicios en frío lentos y pérdida del estado del usuario cuando los jugadores cambian de tarea.
- Finalizaciones a nivel de la plataforma: A partir de Android 17 (nivel de API 37), el sistema es más proactivo a la hora de finalizar los procesos que usan demasiada memoria. Si el espacio que ocupa tu juego es demasiado grande, el SO puede finalizar su proceso de forma abrupta sin generar un seguimiento de pila estándar.
- Compatibilidad con dispositivos: Si bien los dispositivos insignia cuentan con entre 12 GB y 16 GB de RAM, una gran parte del público global de videojuegos usa dispositivos con 4 GB o 6 GB de RAM. La administración adecuada de la memoria garantiza que tu juego siga siendo accesible y responsivo en todos los niveles de hardware sin necesidad de paquetes de recursos complejos y separados.
Información sobre la memoria en Android
Para diseñar estrategias eficaces de presupuesto de memoria, los desarrolladores deben comprender cómo la plataforma de Android administra la memoria física y cómo mide la huella activa de tu juego.
Conceptos básicos de la memoria de Android
Para conocer los conceptos básicos sobre la administración de la memoria a nivel de la plataforma, consulta la documentación oficial de la Descripción general de la administración de la memoria. Este recurso abarca cuatro áreas de la arquitectura:
- Descripción general de la memoria: Android usa la paginación y el mapeo de memoria (mmap) para administrar la RAM. No admite un archivo de intercambio tradicional en el disco, sino que se basa en la compresión de páginas (con zRAM) y la recuperación de páginas para liberar memoria física.
- Asignación de memoria entre procesos: Android comparte la RAM en todo el sistema. Asigna montones específicos para la ejecución de la máquina virtual de Dalvik o ART, a la vez que permite que los entornos de desarrollo nativos (como los motores de juegos de C++) soliciten memoria del montón del sistema nativo.
- Administración de la memoria de la app: Cuando se opera con un modelo de varios procesos, Android espera que las aplicaciones supervisen su estado del ciclo de vida de forma dinámica y liberen de forma voluntaria los recursos innecesarios (como gráficos y mapas de bits no almacenados en caché) para respaldar el estado del sistema.
- Descripción general de los procesos y los subprocesos: El sistema categoriza los procesos en una jerarquía según su visibilidad y su importancia actuales percibidas por el usuario, lo que determina qué procesos se mantienen activos y cuáles se finalizan primero en condiciones de poca memoria.
La métrica de espacio en memoria total
El limitador de memoria de Android 17 a nivel de la plataforma evalúa el consumo de procesos con el uso del espacio en memoria total en lugar del tamaño residente total (RSS) o el tamaño de la memoria virtual.
Espacio en memoria total = RSS anónimo (RssAnon) + intercambio sin comprimir (VmSwap)
Para evitar que los juegos superen los límites de la plataforma, debes comprender con exactitud qué representan estas métricas a nivel del sistema. Para obtener más información sobre estas métricas, las asignaciones de RAM física y cómo se manejan las páginas respaldadas por archivos, consulta Información sobre las métricas de RSS y de intercambio en la guía Supervisa el uso de memoria.
Restricciones de memoria
Para mantener la estabilidad del sistema y garantizar que las aplicaciones no consuman recursos excesivos, la plataforma de Android administra los límites de memoria para los procesos en ejecución.
Limitador de memoria en Android 17 y versiones posteriores
Android 17 (nivel de API 37) y las versiones posteriores administran límites de memoria estrictos por app con cgroup v2 de Linux para evitar que las apps individuales causen inestabilidad en todo el sistema. Para obtener más detalles sobre la implementación técnica, consulta la guía del limitador de memoria del AOSP y la entrada de blog Prioritizing Memory Efficiency: Essential Steps for Android 17.
- Mecanismo: El limitador de memoria supervisa todos los procesos de la aplicación y asigna límites de forma dinámica según el estado del ciclo de vida del proceso:
- Procesos visibles (en primer plano): Se espera que los procesos de la app que muestran una IU ejecuten un conjunto de trabajo de recursos más grande y tengan un límite más generoso.
- Procesos no visibles (en segundo plano o servicios): Los procesos de la app que realizan un trabajo activo sin mostrar una IU se limitan a un presupuesto más estricto y restrictivo.
- Atributos del kernel: El servicio se basa en dos atributos principales:
memory.high: Es un límite flexible. Cuando se supera, el kernel limita el proceso y trata de recuperar memoria de forma agresiva. Esta recuperación puede hacer que el juego experimente una degradación del rendimiento.memory.swap.max: Administra un límite estricto en el espacio de intercambio o zRAM que puede usar el proceso.
- Comportamiento de finalización: Si un proceso sigue asignando memoria
anónima más allá de
memory.highy agota su capacidad de intercambio, las asignaciones fallan y el SO finaliza el proceso de forma silenciosa. Esta finalización se registra conApplicationExitInfoen el motivo de salida Limitador de memoria (disponible a partir de Android 17, 26Q4).
Nuevos límites de memoria en Play Console Vitals
Para ayudar a los desarrolladores a identificar de forma proactiva los problemas de memoria, Google Play presenta nuevas métricas en Android vitals de Play Console. Play Console hace un seguimiento del percentil 90 (P90) del espacio en memoria de RSS anónimo y de intercambio de tus sesiones de juego para identificar valores atípicos extremos.
Los umbrales de advertencia y aplicación de medidas por incumplimiento se ajustan según la capacidad de RAM física del dispositivo y los estados de los procesos. Estos límites se aplican en dos fases distintas.
Para obtener lineamientos detallados y límites de memoria, consulta Android vitals: ¿Cuáles son los umbrales de comportamiento inadecuado?.
Servicios percibidos por el usuario
Los servicios perceptibles son procesos en segundo plano críticos que el sistema Android considera perceptibles para el usuario. Este estado abarca todos los procesos que se están ejecutando:
- Servicios en primer plano (FGS)
- Trabajos acelerados
- Tareas de transferencia de datos iniciadas por el usuario
- Servicios vinculados al sistema o servicios vinculados por otras aplicaciones
Debido a que los servicios perceptibles están diseñados para tareas críticas de larga duración en segundo plano, son muy susceptibles a las pérdidas acumulativas del ciclo de vida. Los límites de memoria de la plataforma de Android tratan este estado como no en primer plano, lo que significa que, si tu juego está en segundo plano, pero sigue ejecutando un servicio perceptible, está sujeto a los límites de memoria de servicio o en segundo plano más estrictos que se muestran en la página del Centro de ayuda de Play. Los juegos deben recortar de forma agresiva los recursos innecesarios cuando pasan del primer plano a un estado de servicio perceptible en segundo plano.
Requisitos de R8
Para minimizar el tamaño del código de bytes y reducir la sobrecarga básica del proceso de Java, Google Play Console evalúa la optimización del código como parte de sus lineamientos de calidad de las apps. Para obtener más información sobre los requisitos y límites de optimización que se aplican a los juegos, consulta Optimización de código DEX en la documentación de Android vitals.
Para configurar R8 en un proyecto de Android estándar, sigue la guía Habilita la optimización de apps. Para habilitar la configuración avanzada de reducción y optimización, consulta Cómo usar R8 en modo completo. Para identificar qué reglas impiden que R8 ofusque clases o quite código muerto, usa el Analizador de configuración de R8.
Si compilas con un motor de juego, el comportamiento predeterminado de R8 varía según el motor y la versión:
- Unity: La reducción de código está inhabilitada de forma predeterminada, por lo que debes habilitarla en la configuración del proyecto. Para obtener más información sobre la compatibilidad de versiones y los pasos de configuración, consulta Cómo reducir y optimizar el código de Unity con R8.
- Unreal Engine: A partir de la versión 4.25 de UE, R8 está habilitado de forma predeterminada en las compilaciones de lanzamiento, lo que requiere que administres las reglas de conservación para los puentes y complementos de JNI. Para obtener más información sobre los detalles de la versión y la configuración de UPL, consulta Cómo reducir y optimizar el código de Unreal Engine con R8.
Requisitos de los mapas de bits
Los mapas de bits representan una gran parte del uso de memoria en los juegos modernos de alta fidelidad. Debido a que los datos de píxeles de Bitmap se almacenan directamente en el montón nativo no administrado en Android 8.0 (nivel de API 26) y versiones posteriores, la carga de imágenes no optimizada puede hacer que los procesos superen los umbrales de memoria de la plataforma. Para conocer las prácticas recomendadas sobre el escalamiento y el almacenamiento en caché de imágenes, consulta Optimiza el uso de imágenes.
Supervisa el uso de memoria
Para optimizar de manera eficaz la memoria de tu juego, primero debes comprender cómo la plataforma de Android mide su huella. Android 17 actualiza la métrica de memoria
para hacer un seguimiento de la suma de RSS anónimo (RssAnon) y el espacio de intercambio sin comprimir (VmSwap), sin incluir la memoria respaldada por archivos ni la privada de la GPU. En esta guía, se detalla
cómo usar herramientas a nivel del sistema, como Perfetto y meminfo, implementar
APIs de diagnóstico, como ProfilingManager y onTrimMemory, y extraer
asignaciones de memoria precisas en Unity y Unreal Engine. Comprende cómo
generar perfiles de tu juego con precisión y evitar las interrupciones de rendimiento asociadas con
el sondeo tradicional de memoria en el entorno de ejecución.
Para obtener más información, consulta Supervisa el uso de memoria.
Estrategias de reducción de memoria
Si bien los motores de juegos simplifican el desarrollo multiplataforma, su administración de memoria predeterminada puede activar límites de memoria a nivel del SO. En esta página, se detallan los pasos prácticos de optimización diseñados específicamente para Unity y Unreal Engine. Comprende
por qué depender de onTrimMemory basado en Java puede causar interbloqueos en Unity y cómo
usar devoluciones de llamada de ciclo de vida nativas en su lugar. También descubrirás optimizaciones clave a nivel de los recursos, como usar la compresión de texturas ASTC 8x8 y configurar las descargas de recursos, para que tu juego se ejecute sin problemas en todos los niveles de hardware.
Para obtener más información, consulta Reduce el uso de memoria.