Gerenciar a memória em jogos de forma eficiente

Na plataforma Android, o sistema tenta usar o máximo de memória do sistema (RAM, na sigla em inglês) possível e executa várias otimizações de memória para liberar espaço quando necessário. Essas otimizações podem ter um efeito negativo no jogo, na lentidão ou à disposição total. Saiba mais sobre essas otimizações no tópico Alocação de memória entre processos.

Esta página explica as etapas que você pode seguir para evitar que condições de pouca memória afetem seu jogo.

Responda ao onTrimMemory()

O sistema usa onTrimMemory() para notificar seu app sobre eventos de ciclo de vida que oferecem uma boa oportunidade para seu para reduzir voluntariamente seu uso de memória e evitar ser eliminado pelo low-memory killer (LMK) para liberar memória para outros apps usarem.

Se o app for encerrado em segundo plano, na próxima vez que o usuário iniciar seu app, eles enfrentarão uma lentidão inicialização a frio. Apps que reduzem a uso da memória em segundo plano têm menos chances de serem eliminados no plano de fundo.

Ao responder a eventos de corte, é melhor liberar grandes alocações de memória que não são imediatamente necessários e podem ser reconstruídos sob demanda. Para exemplo, se o app tiver um cache de bitmaps que foram decodificados localmente imagens compactadas armazenadas, geralmente é uma boa ideia cortar ou limpar cache em resposta a TRIM_MEMORY_UI_HIDDEN

Kotlin

class MainActivity : AppCompatActivity(), ComponentCallbacks2 {
    override fun onTrimMemory(level: Int) {
        if (level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
            // Release memory related to UI elements, such as bitmap caches.
        }
        if (level >= ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) {
            // Release memory related to background processing, such as by
            // closing a database connection.
        }
    }
}

Java

public class MainActivity extends AppCompatActivity implements ComponentCallbacks2 {
    public void onTrimMemory(int level) {
        switch (level) {
            if (level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
                // Release memory related to UI elements, such as bitmap caches.
            }
            if (level >= ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) {
                // Release memory related to background processing, such as by
                // closing a database connection.
            }
        }
    }
}

C#

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

class LowMemoryTrigger : MonoBehaviour
{
    private void Start()
    {
        Application.lowMemory += OnLowMemory;
    }
    private void OnLowMemory()
    {
        // Respond to low memory condition (e.g., Resources.UnloadUnusedAssets())
    }
}

Usar a API Memory Advice Beta

A API Memory Advice foi desenvolvida como um alternativa ao onTrimMemory, que tem recall e precisão muito maiores na previsão de LMKs iminentes. Para isso, a API estima a quantidade de dos recursos de memória em uso e notificar o aplicativo quando determinados os limites mínimos sejam excedidos. A API também informa a porcentagem estimada de uso de memória diretamente em seu aplicativo. É possível usar a API Memory Advice como como alternativa onTrimMemory para fins de gerenciamento de memória.

Para usar a API Memory Advice, veja o Guia explicativo.

Conservar a capacidade da memória

Gerencie a memória de maneira conservadora para evitar a falta de memória. Veja alguns itens a serem considerados:

  • Tamanho da RAM física: os jogos geralmente usam entre ¼ e ½ do valor da RAM física no dispositivo.
  • Tamanho máximo da zRAM: mais zRAM significa que o jogo pode ter mais memória para alocar. Esse valor pode variar de acordo com o dispositivo. Procure SwapTotal em /proc/meminfo para encontrar esse valor.
  • Uso da memória do SO: os dispositivos que designam mais RAM para os processos do sistema deixam menos memória para o jogo. O sistema encerra o processo do jogo antes de encerrar processos do sistema.
  • Uso de memória dos apps instalados: teste seu jogo em dispositivos que têm muitos apps instalados. Os apps de redes sociais e chat precisam ser executados constantemente e afetar a quantidade de memória livre.

Se não for possível usar uma capacidade de memória conservadora, tente uma abordagem mais flexível. Se o sistema tiver problemas de pouca memória, reduza a quantidade de memória que o jogo está usando. Por exemplo, aloque texturas de baixa resolução ou armazene menos sombreadores em resposta a onTrimMemory(). Essa abordagem dinâmica para a alocação de memória requer mais trabalho do desenvolvedor, especialmente na fase de design do jogo.

Evite a sobrecarga

A sobrecarga ocorre quando a memória livre é baixa, mas não o suficiente para encerrar o jogo. Nessa situação, kswapd recuperou páginas de que o jogo ainda precisa, por isso, tenta recarregar as páginas da memória. Não há espaço suficiente, então as páginas continuam sendo trocadas (troca contínua). O rastreamento do sistema informa essa situação como uma linha de execução em que kswapd é executado continuamente.

Um sintoma de sobrecarga é um tempo longo para a renderização do frame, possivelmente um segundo ou mais. Reduza o consumo de memória do jogo para resolver essa situação.

Use as ferramentas disponíveis

O Android tem uma coleção de ferramentas que ajudam a entender como o sistema gerencia a memória.

Meminfo

Essa ferramenta coleta estatísticas sobre a memória para mostrar quanta memória PSS foi alocada e as categorias para as quais ela foi usada.

Veja as estatísticas do meminfo de uma das seguintes maneiras:

  • Use o comando adb shell dumpsys meminfo package-name;
  • Use a chamada MemoryInfo da API Android Debug.

A estatística PrivateDirty mostra a quantidade de RAM dentro do processo que não pode ser paginada no disco e não é compartilhada com outros processos. A maior parte desse valor fica disponível para o sistema quando esse processo é encerrado.

Tracepoints de memória

Os tracepoints de memória rastreiam a quantidade de memória RSS que seu jogo está usando. Calcular o uso de memória RSS é muito mais rápido do que calcular o uso do PSS. Como é mais rápido de calcular, o RSS mostra uma granularidade mais refinada nas alterações no tamanho da memória para medições mais precisas do uso de memória de pico. Portanto, é mais fácil observar picos que possam fazer com que o jogo fique sem memória.

Perfetto e rastreamentos longos

O Perfetto (link em inglês) é um pacote de ferramentas para coletar informações de desempenho e memória de um dispositivo e exibi-lo em uma IU baseada na Web. É compatível com rastreamentos arbitrariamente longos para que você possa visualizar como o RSS muda ao longo do tempo. Também é possível emitir consultas SQL dos dados produzidos para processamento off-line. Ative os rastreamentos longos no app Rastreamento do sistema. Verifique se a categoria memory:Memory está ativada para o rastreamento.

Heapprofd

heapprofd é uma ferramenta de rastreamento de memória que faz parte do Perfetto. Essa ferramenta pode ajudar a encontrar vazamentos de memória ao mostrar onde a memória foi alocada usando malloc. O heapprofd pode ser iniciado usando um script Python. Como a ferramenta tem baixa sobrecarga, isso não afeta o desempenho como outras ferramentas, como a Malloc Debug.

Relatório de bugs

bugreport é uma ferramenta de geração de registros para descobrir se o jogo falhou porque ficou sem memória. A saída da ferramenta é muito mais detalhada do que a do logcat. Ele é útil para depuração de memória porque mostra se o jogo falhou por falta de memória ou se foi encerrado pelo LMK.

Para mais informações, consulte Capturar e ler relatórios de bugs.