Sobre o gerenciamento de memória

A otimização de memória é essencial para oferecer experiências de jogo estáveis e de alto desempenho no Android. Este guia explica por que a eficiência de memória é importante, como o sistema operacional Android gerencia os limites de memória de processos e as novas métricas de memória no Google Play Console para ajudar você a monitorar e melhorar a qualidade técnica do seu jogo.

A importância da otimização de memória

Otimizar a memória do jogo é essencial para manter a retenção de jogadores, ampliar a compatibilidade com dispositivos e obedecer aos padrões de qualidade da plataforma:

  • Prevenção de inicialização a frio (experiência e retenção do usuário): quando um jogador sai temporariamente do jogo (por exemplo, para responder a uma notificação ou verificar uma mensagem), o sistema operacional coloca o processo do jogo em segundo plano. Se o consumo de memória em segundo plano do jogo for muito alto, o Low Memory Killer (LMK) do sistema prioriza o encerramento do processo do jogo para recuperar RAM para tarefas em primeiro plano. Na próxima vez que o usuário retomar o jogo, em vez de uma retomada rápida e instantânea, o jogo precisará passar por uma inicialização a frio longa, recarregando completamente recursos gráficos pesados, áudio e binários do mecanismo de jogo do armazenamento. Manter o uso da memória em segundo plano baixo evita essas finalizações silenciosas, preservando o estado do usuário e garantindo que os jogadores possam retomar a sessão imediatamente. Para mais detalhes sobre o comportamento do LMK do sistema, consulte o guia Android vitals: Low memory killers.
  • Estabilidade do ecossistema e do dispositivo: o uso ineficiente da memória e os vazamentos de memória prejudicam a integridade geral do sistema. Quando a memória do sistema é escassa, o sistema enfrenta uma pressão severa, resultando em quedas na taxa de frames, instabilidade na interface e falhas de áudio. Se a pressão de memória for muito alta, o Low Memory Killer (LMK) do sistema vai encerrar os processos em segundo plano de forma agressiva, forçando outros aplicativos a passar por inicializações a frio lentas e perda de estado do usuário quando os jogadores alternarem entre tarefas.
  • Encerramentos no nível da plataforma: a partir do Android 17 (nível 37 da API), o sistema é mais proativo ao encerrar processos que usam muita memória. Se o consumo do jogo for muito alto, o SO poderá encerrar o processo abruptamente sem gerar um stack trace padrão.
  • Compatibilidade do dispositivo: embora os dispositivos de última geração tenham de 12 GB a 16 GB de RAM, uma grande parte do público global de jogos usa dispositivos com 4 GB ou 6 GB de RAM. O gerenciamento de memória adequado garante que o jogo permaneça acessível e responsivo em todos os níveis de hardware sem exigir pacotes de recursos complexos e separados.

Noções básicas sobre memória no Android

Para criar estratégias eficazes de orçamento de memória, os desenvolvedores precisam entender como a plataforma Android gerencia a memória física e como ela mede o consumo ativo do jogo.

Conceitos básicos de memória do Android

Para conceitos básicos sobre o gerenciamento de memória no nível da plataforma, consulte a documentação oficial de Visão geral do gerenciamento de memória. Este recurso abrange quatro áreas de arquitetura:

  • Visão geral da memória: o Android usa paginação e mapeamento em memória (mmap) para gerenciar a RAM. Ele não é compatível com um arquivo de troca tradicional no disco. Em vez disso, ele depende da compactação de páginas (usando zRAM) e da recuperação de páginas para liberar memória física.
  • Alocação de memória nos processos: o Android compartilha a RAM em todo o sistema. Ele atribui heaps específicos para a execução da máquina virtual Dalvik ou ART, permitindo que ambientes de desenvolvimento nativos (como mecanismos de jogo em C++) solicitem memória do heap do sistema nativo.
  • Gerenciamento de memória do app: operando em um modelo de vários processos, o Android espera que os aplicativos monitorem o estado do ciclo de vida de forma dinâmica e liberem voluntariamente recursos desnecessários (como gráficos e bitmaps não armazenados em cache) para manter a integridade do sistema.
  • Visão geral de processos e linhas de execução: o sistema categoriza os processos em uma hierarquia com base na visibilidade e importância atuais percebidas pelo usuário, determinando quais processos são mantidos ativos e quais são encerrados primeiro durante condições de pouca memória.

A métrica de consumo total de memória

O Limitador de Memória do Android 17 no nível da plataforma avalia o consumo de processos usando o consumo total de memória, em vez do tamanho residente total (RSS, na sigla em inglês) ou do tamanho da memória virtual.

Consumo total de memória = RSS anônimo (RssAnon) + espaço de troca não compactado (VmSwap)

Para evitar que os jogos excedam os limites da plataforma, é preciso entender exatamente o que essas métricas representam no nível do sistema. Para mais informações sobre essas métricas, alocações de RAM física e como as páginas com backup de arquivos são processadas, consulte Noções básicas sobre métricas de RSS e troca no guia Monitorar o uso da memória.

Restrições de memória

Para manter a estabilidade do sistema e garantir que os aplicativos não consumam recursos excessivos, a plataforma Android gerencia os limites de memória para processos em execução.

Limitador de memória no Android 17 e versões mais recentes

O Android 17 (nível 37 da API) e versões mais recentes gerenciam limites de memória rígidos por app usando o cgroup v2 do Linux para evitar que apps individuais causem instabilidade em todo o sistema. Para mais detalhes sobre a implementação técnica, consulte o Guia do limitador de memória do AOSP e o blog Prioritizing Memory Efficiency: Essential Steps for Android 17 (em inglês).

  • Mecanismo: o Limitador de Memória monitora todos os processos do aplicativo e atribui limites dinamicamente com base no estado do ciclo de vida do processo:
    • Processos visíveis (em primeiro plano): os processos de apps que mostram uma interface precisam executar um conjunto de trabalho de recursos maior e recebem um limite mais generoso.
    • Processos não visíveis (em segundo plano ou serviços): os processos do app que realizam trabalho ativo sem mostrar uma interface são limitados a um orçamento mais restrito.
  • Atributos do kernel: o serviço depende de dois atributos principais:
    • memory.high: um limite flexível. Quando excedido, o kernel limita o processo e tenta recuperar a memória de forma agressiva. Essa recuperação pode causar uma degradação do desempenho do jogo.
    • memory.swap.max: gerencia um limite rígido no espaço de troca ou zRAM que o processo pode usar.
  • Comportamento de encerramento: se um processo continuar a alocar memória anônima além de memory.high e esgotar a capacidade de troca, as alocações vão falhar e o SO vai encerrar o processo silenciosamente. Esse encerramento é registrado usando ApplicationExitInfo no motivo de saída Limitador de memória (disponível a partir do Android 17, 26Q4).

Novos limites de memória no Android vitals do Play Console

Para ajudar os desenvolvedores a identificar proativamente problemas de memória, o Google Play está introduzindo novas métricas no Android vitals do Play Console. O Play Console rastreia o consumo de memória RSS anônimo + espaço de troca do 90º percentil (P90) das sessões de jogo para identificar outliers extremos.

Os limites de aviso e aplicação são escalonados com base na capacidade física de RAM do dispositivo e nos estados do processo. Esses limites se aplicam em duas fases distintas.

Para diretrizes detalhadas e limites de memória, consulte Android vitals: quais são os limites de mau comportamento?.

Serviços percebidos pelo usuário

Os serviços perceptíveis são processos críticos em segundo plano que o sistema Android considera perceptíveis para o usuário. Esse estado abrange todos os processos em execução:

  • Serviços em primeiro plano (FGS)
  • Jobs acelerados
  • Jobs de transferência de dados iniciados pelo usuário
  • Serviços vinculados ao sistema ou a outros aplicativos

Como os serviços perceptíveis são projetados para tarefas críticas e de longa duração em segundo plano, eles são altamente suscetíveis a vazamentos cumulativos de ciclo de vida. Os limites de memória da plataforma Android tratam esse estado como não sendo de primeiro plano. Isso significa que, se o jogo estiver em segundo plano, mas continuar executando um serviço perceptível, ele estará sujeito aos limites de memória mais rígidos de segundo plano ou de serviço mostrados na página da Central de Ajuda do Google Play. Os jogos precisam reduzir agressivamente os recursos desnecessários ao passar do primeiro plano para um estado de serviço perceptível em segundo plano.

Requisitos do R8

Para minimizar o tamanho do bytecode e reduzir a sobrecarga básica do processo Java, o Google Play Console avalia a otimização de código como parte das diretrizes de qualidade do app. Para mais informações sobre os requisitos e limites de otimização que se aplicam a jogos, consulte Otimização de código DEX na documentação do Android vitals.

Para configurar o R8 em um Projeto do Android padrão, siga o guia Ativar a otimização de apps. Para ativar as configurações avançadas de redução e otimização, consulte Usar o R8 no modo completo. Para identificar quais regras estão impedindo o R8 de ofuscar classes ou remover código inativo, use o R8 Configuration Analyzer.

Se você estiver criando com um mecanismo de jogo, o comportamento padrão do R8 vai variar de acordo com o mecanismo e a versão:

  • Unity: a minificação de código está desativada por padrão. Portanto, é necessário ativá-la nas configurações do projeto. Para mais informações sobre o suporte a versões e as etapas de configuração, consulte Reduzir e otimizar o código do Unity com o R8.
  • Unreal Engine: a partir do UE 4.25, o R8 é ativado por padrão em builds de lançamento, exigindo que você gerencie regras de manutenção para pontes e plug-ins da JNI. Para mais informações sobre detalhes da versão e configuração da UPL, consulte Reduzir e otimizar o código do Unreal Engine com o R8.

Requisitos de bitmap

Os bitmaps representam uma parte enorme do uso da memória em jogos modernos de alta fidelidade. Como os dados de pixel do bitmap são armazenados diretamente no heap nativo não gerenciado no Android 8.0 (nível da API 26) e versões mais recentes, o carregamento de imagens não otimizadas pode fazer com que os processos ultrapassem os limites de memória da plataforma. Para conferir as práticas recomendadas de dimensionamento e armazenamento em cache de imagens, consulte Otimizar o uso de imagens.

Monitorar o uso da memória

Para otimizar a memória do jogo de forma eficaz, primeiro você precisa entender como a plataforma Android mede o consumo. O Android 17 atualiza a métrica de memória para rastrear a soma do RSS anônimo (RssAnon) e do espaço de troca descompactado (VmSwap), excluindo a memória com backup de arquivos ou particular da GPU. Este guia detalha como usar ferramentas no nível do sistema, como o Perfetto e o meminfo, implementar APIs de diagnóstico, como ProfilingManager e onTrimMemory, e extrair alocações de memória precisas no Unity e no Unreal Engine. Entenda como criar um perfil preciso do jogo e evitar as interrupções de desempenho associadas à sondagem de memória de execução tradicional.

Para mais informações, consulte Monitorar o uso da memória.

Estratégias de redução de memória

Embora os mecanismos de jogo simplifiquem o desenvolvimento multiplataforma, o processamento de memória padrão deles pode acionar limites de memória no nível do SO. Esta página detalha as etapas práticas de otimização especificamente adaptadas para o Unity e o Unreal Engine. Entenda por que confiar em onTrimMemory baseado em Java pode causar impasses no Unity e como usar callbacks do ciclo de vida nativos. Você também vai descobrir as principais otimizações no nível do recurso, como usar a compactação de textura ASTC 8x8 e configurar descarregamentos de recursos, para manter o jogo funcionando sem problemas em todos os níveis de hardware.

Para mais informações, consulte Reduzir o uso da memória.