Como verificar o uso da memória com as ferramentas do Unity

No sistema Android, o limite de memória para o Memory Limiter (Android 17 e mais recentes) e o valor de referência para a métrica de memória no Google Play Console é 'RSS anônimo + espaço de troca'. Esse valor representa o nível de pressão de memória física realmente ocupado pelo processo e é uma métrica essencial que precisa ser monitorada para uma operação estável do app.

No momento, é difícil medir o uso da memória que corresponda exatamente a "RSS anônimo + espaço de troca" no tempo de execução ou estaticamente no Unity Engine.

Para resolver esse problema, esta página apresenta como rastrear o valor de 'RSS anônimo + espaço de troca' com alta confiabilidade por aproximação usando o Unity Profiler, a API ProfileRecorder e o Unity Memory Profiler em um ambiente do Unity Engine.

Verificar o uso da memória no tempo de execução

No Unity, medir diretamente o uso de memória exato de 'RSS anônimo + espaço de troca' do Android no tempo de execução sem sobrecarga pode ser desafiador. No entanto, é possível estimar esse valor (com uma variação de aproximadamente 10%) usando as APIs do Profiler integradas do Unity.

Método 1: usar o Profiler

Você pode estimar o uso da memória total somando os valores retornados pelas seguintes APIs do 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: usar a classe ProfilerRecorder

Você pode rastrear as métricas Total Reserved Memory e Gfx Reserved Memory usando o ProfilerRecorder:

  • Em builds de desenvolvimento: calcule 'Total Reserved Memory' - 'Gfx Reserved Memory'.

  • Em builds de lançamento: use 'Total Reserved Memory' diretamente.

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;
    }
}

Limitação:

  • As alocações de memória que ignoram o gerenciador de memória do Unity (por exemplo, NativeMalloc(IntPtr size)) não podem ser rastreadas por essas APIs.

  • Essas APIs podem não ser compatíveis, dependendo da versão do Unity.

Analisar a distribuição de memória (análise estática)

Se um uso da memória elevado for detectado no tempo de execução, você precisará identificar onde a memória está sendo consumida. A análise da distribuição de memória permite eliminar alocações desnecessárias e reduzir a ocupação geral de memória do jogo.

Unity Memory Profiler

Para realizar uma análise detalhada da memória e otimizar seu jogo, use o pacote Unity Memory Profiler. Para localizar os itens correspondentes ao uso de 'RSS anônimo + espaço de troca' do SO Android:

  1. Capture um snapshot usando o Unity Memory Profiler.
  2. Abra o snapshot e navegue até All of Memory > Resident Memory on Device.
  3. Calcule a soma dos seguintes itens:

    • Untracked
    • Android Runtime
    • Nativo
    • Gerenciado
    Métricas de memória visíveis quando definidas como "Memória residente" no Unity Memory Profiler
    Figura 1. As descrições de memória são armazenadas na memória residente

A soma total serve como um substituto prático para o 'RSS anônimo + espaço de troca' do SO Android, oferecendo insights acionáveis para a análise de memória do jogo.

Limitação:

  • Em caso de pressão de memória intensa, o SO Android pode compactar ou trocar páginas de memória ativas para liberar RAM. Como o Unity Memory Profiler não pode rastrear a memória trocada, podem surgir discrepâncias durante cenários de uso da memória elevado.

  • Esses recursos podem ser limitados, dependendo da versão do Unity.

Exemplo de scripts em C#

Consulte os AndroidAndroidProcessStats.cs e GameMemoryMonitor.cs scripts para exemplos de como monitorar o estado e as ocupações de memória dos processos do jogo no tempo de execução.

  • O script AndroidProcessStats demonstra como usar a JNI (Java Native Interface) para vincular às APIs ActivityManager do Android e fornece funcionalidade para buscar processos em execução, a importância deles e o estado no nível do SO. Ele também contém métodos leves para ler estatísticas de memória diretamente do sistema de arquivos /proc do Linux.
  • O script GameMemoryMonitor mostra como conectar essas informações para monitoramento e depuração contínuos, incluindo como consultar as APIs do Android em uma linha de execução em segundo plano para evitar sobrecarga de desempenho (já que o uso da JNI e a consulta de informações de memória podem ser caros) e como se conectar a eventos de ciclo de vida, como OnApplicationPause, para rastrear mudanças de memória quando o app é colocado em segundo plano ou retomado.

Para usar essas ferramentas, adicione os dois scripts à pasta "Assets" e anexe o script GameMemoryMonitor a um GameObject na cena.

Avaliação mais precisa

Para verificar os valores exatos de 'RSS anônimo + espaço de troca' do SO Android, use Perfetto. Ele permite medir o uso da memória preciso no nível do sistema e analisar detalhadamente o consumo de memória do jogo.

Interface do Perfetto mostrando RSS anônimo, troca e a consulta de soma deles.
Figura 2. A interface do Perfetto mostrando o RSS anônimo, o espaço de troca e a consulta de soma.