Cómo verificar el uso de memoria con las herramientas de Unity

En el sistema Android, el umbral de límite de memoria para el limitador de memoria (Android 17+) y el valor de referencia para la métrica de memoria vital en Google Play Console es 'RSS anónimo + intercambio'. Este valor representa el nivel de presión de memoria física que ocupa el proceso y es una métrica fundamental que se debe supervisar para que la app funcione de forma estable.

Actualmente, es difícil medir el uso de memoria que coincida exactamente con "RSS anónimo + intercambio" en el tiempo de ejecución o de forma estática dentro de Unity Engine.

Para abordar este problema, en esta página, se explica cómo hacer un seguimiento del valor "RSS anónimo + intercambio" con alta confiabilidad a través de la aproximación con Unity Profiler, la API de ProfileRecorder y Unity Memory Profiler en un entorno de Unity Engine.

Cómo comprobar el uso de memoria en el tiempo de ejecución

En Unity, medir directamente el uso exacto de memoria 'RSS anónimo + intercambio' en el tiempo de ejecución sin sobrecarga puede ser un desafío. Sin embargo, puedes estimar este valor de cerca (con una variación de aproximadamente el 10%) con las APIs de Profiler integradas de Unity.

Método 1: Usa el generador de perfiles

Para estimar el uso total de memoria, suma los valores que muestran las siguientes APIs de Profiler:

using UnityEngine;
using UnityEngine.Profiling;

public class MemoryMonitor : MonoBehaviour
{
    public long GetEstimatedMemoryUsageBytes()
    {
        long totalReserved = Profiler.GetTotalReservedMemoryLong();
        long monoHeapSize = Profiler.GetMonoHeapSizeLong();

        return totalReserved + monoHeapSize;
    }
}

Método 2: Usa la clase ProfilerRecorder

Puedes hacer un seguimiento de las métricas Total Reserved Memory y Gfx Reserved Memory con ProfilerRecorder:

  • En las compilaciones de desarrollo, calcula 'Total Reserved Memory' - 'Gfx Reserved Memory'.

  • En las compilaciones de lanzamiento, usa 'Total Reserved Memory' directamente.

using Unity.Profiling;
using UnityEngine;

public class MemoryMonitor : MonoBehaviour
{
    ProfilerRecorder totalRecorder;

#if DEVELOPMENT_BUILD
    ProfilerRecorder gfxRecorder;
#endif

    private void OnEnable()
    {
        totalRecorder = ProfilerRecorder.StartNew(ProfilerCategory.Memory, "Total Reserved Memory");

#if DEVELOPMENT_BUILD
        gfxRecorder = ProfilerRecorder.StartNew(ProfilerCategory.Memory, "Gfx Reserved Memory");
#endif
    }

    private void OnDisable()
    {
        totalRecorder.Dispose();

#if DEVELOPMENT_BUILD
        gfxRecorder.Dispose();
#endif
    }

    public long GetEstimatedMemoryUsageBytes()
    {
#if DEVELOPMENT_BUILD
        if (totalRecorder.Valid && gfxRecorder.Valid)
        {
            return totalRecorder.LastValue - gfxRecorder.LastValue;
        }
#else
        if (totalRecorder.Valid)
        {
            return totalRecorder.LastValue;
        }
#endif
        return 0;
    }
}

Limitación:

  • Estas APIs no pueden hacer un seguimiento de las asignaciones de memoria que omiten el administrador de memoria de Unity (p.ej., NativeMalloc(IntPtr size)).

  • Es posible que estas APIs no sean compatibles según tu versión de Unity.

Analiza el desglose de la memoria (análisis estático)

Si se detecta un uso elevado de memoria en el tiempo de ejecución, debes identificar dónde se consume la memoria. Analizar la distribución de la memoria te permite eliminar las asignaciones innecesarias y reducir el espacio en memoria general del juego.

Generador de perfiles de memoria de Unity

Para realizar un análisis de memoria detallado y optimizar tu juego, usa el paquete Generador de perfiles de memoria de Unity. Para ubicar los elementos correspondientes al uso de 'RSS anónimo + intercambio' del SO Android, haz lo siguiente:

  1. Captura una instantánea con Unity Memory Profiler.
  2. Abre la instantánea y navega a All of Memory > Resident Memory on Device.
  3. Calcula la suma de los siguientes elementos:

    • Untracked
    • Android Runtime
    • Nativo
    • Administrado
    Métricas de memoria que se pueden ver cuando se establece en Memoria residente en el Generador de perfiles de memoria de Unity
    Figura 1. Las descripciones de la memoria se almacenan en la memoria residente.

La suma total sirve como un sustituto práctico para 'RSS anónimo + intercambio' del SO Android, ya que proporciona estadísticas prácticas para el análisis de la memoria del juego.

Limitación:

  • Bajo una gran presión de memoria, el SO Android puede comprimir o intercambiar páginas de memoria activas para liberar RAM. Debido a que Unity Memory Profiler no puede hacer un seguimiento de la memoria intercambiada, pueden surgir discrepancias durante situaciones de uso elevado de memoria.

  • Es posible que estas funciones estén limitadas según tu versión de Unity.

Ejemplos de secuencias de comandos en C#

Puedes consultar las secuencias de comandos AndroidAndroidProcessStats.cs y GameMemoryMonitor.cs para obtener ejemplos sobre cómo supervisar el estado y las huellas de memoria de los procesos de tu juego en el tiempo de ejecución.

  • La secuencia de comandos AndroidProcessStats muestra cómo usar JNI (Java Native Interface) para vincularse a las APIs de ActivityManager de Android y proporciona funcionalidad para recuperar procesos en ejecución, su importancia y el estado a nivel del SO. También contiene métodos ligeros para leer estadísticas de memoria directamente desde el sistema de archivos /proc de Linux.
  • La secuencia de comandos GameMemoryMonitor muestra cómo conectar esta información para la supervisión y depuración continuas, incluido cómo sondear las APIs de Android en un subproceso en segundo plano para evitar la sobrecarga de rendimiento (ya que usar JNI y sondear información de memoria puede ser costoso) y cómo conectarse a eventos de ciclo de vida como OnApplicationPause para hacer un seguimiento de los cambios de memoria cuando la app se ejecuta en segundo plano o se reanuda.

Para usar estas herramientas, agrega ambas secuencias de comandos a tu carpeta Assets y adjunta la secuencia de comandos GameMemoryMonitor a un GameObject en tu escena.

Evaluación más precisa

Para verificar los valores exactos de 'RSS anónimo + intercambio' del SO Android, usa Perfetto. Te permite medir el uso preciso de memoria a nivel del sistema y analizar minuciosamente la huella de memoria de tu juego.

IU de Perfetto que muestra la RSS anónima, el intercambio y su consulta de suma.
Figura 2. La IU de Perfetto muestra el RSS anónimo, el intercambio y su consulta de suma.