A otimização da memória é fundamental para oferecer experiências de jogos estáveis e de alta performance no Android. Este guia oferece uma visão geral da importância da eficiência de memória, de como o sistema operacional Android gerencia os limites de memória do processo e das 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, expandir a compatibilidade de dispositivos e obedecer aos padrões de qualidade da plataforma:
- Prevenção de inicialização a frio (experiência do usuário e retenção): 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 vai priorizar o encerramento do processo do jogo para recuperar a RAM para tarefas em primeiro plano. Na próxima vez que o usuário retomar, em vez de uma retomada instantânea e perfeita, o jogo vai 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 esses encerramentos silenciosos em segundo plano, 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 degradam a saúde geral do sistema. Quando a memória do sistema é escassa, ele enfrenta uma pressão severa, resultando em quedas na taxa de frames, instabilidade da interface e falhas de áudio. Se a pressão de memória for muito severa, o Low Memory Killer (LMK) do sistema vai encerrar agressivamente os processos em segundo plano, forçando outros aplicativos a passar por inicializações a frio lentas e perda do estado do usuário quando os jogadores alternam entre tarefas.
- Encerramentos no nível da plataforma: a partir do Android 17 (nível 37 da API), o sistema é mais proativo no encerramento de 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 de dispositivos: embora os dispositivos principais 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 adequado da memória garante que o jogo permaneça acessível e responsivo em todas as categorias de hardware sem exigir pacotes de recursos complexos e separados.
Entender a 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.
Principais conceitos 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 Visão geral do gerenciamento de memória. Esse recurso abrange quatro áreas arquitetônicas:
- Visão geral da memória: o Android usa paginação e mapeamento de memória (mmap) para gerenciar a RAM. Ele não oferece suporte a 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 entre 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 nativo (como mecanismos de jogos 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 oferecer suporte à saúde 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 percebidas pelo usuário, determinando quais processos são mantidos ativos e quais são encerrados primeiro em condições de pouca memória.
A métrica de consumo total de memória
O Memory Limiter do Android 17 no nível da plataforma avalia o consumo do processo usando o consumo total de memória em vez do tamanho residente total (RSS) ou do tamanho da memória virtual.
Consumo total de memória = RSS anônimo (RssAnon) + troca não compactada (VmSwap)
Para evitar que os jogos excedam os limites da plataforma, é necessário 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 suporte de arquivo são processadas, consulte Entender as métricas de RSS e troca no guia Monitorar o uso de 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.
Memory Limiter 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 estritos 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 Memory Limiter do AOSP e o artigo Priorizar a eficiência de memória: etapas essenciais para o Android 17 (links em inglês).
- Mecanismo: o Memory Limiter 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 de apps que fazem trabalhos ativos sem mostrar uma interface são restritos a um orçamento mais apertado e restritivo.
- 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 degradação de performance no jogo.memory.swap.max: gerencia um limite máximo no espaço de troca ou zRAM que o processo pode usar.
- Comportamento de encerramento: se um processo continuar alocando memória anônima
após
memory.highe esgotar a capacidade de troca, as alocações vão falhar, e o SO vai encerrar o processo silenciosamente. Esse encerramento é registrado usandoApplicationExitInfono motivo de saída do Memory Limiter (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 problemas de memória de forma proativa, o Google Play está lançando novas métricas no Android Vitals do Play Console. O Play Console rastreia o 90º percentil (P90) do consumo de memória RSS anônimo + troca das sessões de jogo para identificar valores atípicos extremos.
Os limites de aviso e aplicação são dimensionados com base na capacidade de RAM física e nos estados do processo do dispositivo. Esses limites são aplicados em duas fases distintas.
Para diretrizes detalhadas e limites de memória, consulte o Android vitals: What are the bad behavior thresholds?.
Serviços percebidos pelo usuário
Os serviços perceptíveis são processos críticos em segundo plano que o sistema Android considera notá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 vinculados por 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 do ciclo de vida. Os limites de memória da plataforma Android tratam esse estado como não em 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 de serviço ou em segundo plano mais apertados 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 do código como parte das diretrizes de qualidade do app. Para mais detalhes sobre como configurar o pipeline de build, consulte o guia Ativar a otimização de apps com o R8.
Para configurar o R8 no seu projeto, siga o guia Ativar a otimização de apps com o R8. 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 que o R8 ofusque classes ou remova código inativo, use o Analisador de configuração do R8.
Requisitos de bitmap
Os bitmaps representam uma grande parte 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 otimizado pode levar os processos a ultrapassar os limites de memória da plataforma. Para conferir as práticas recomendadas sobre escalonamento 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 é necessário 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 da troca não compactada (VmSwap), excluindo a memória com suporte de arquivo ou privada da GPU. Este guia detalha como aproveitar 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 instabilidades de performance associadas à pesquisa tradicional de memória de execução.
Para mais informações, consulte Monitorar o uso de memória.
Estratégias de redução de memória
Embora os mecanismos de jogos simplifiquem o desenvolvimento multiplataforma, o gerenciamento de memória padrão deles pode acionar limites de memória no nível do SO. Esta página detalha etapas práticas de otimização especificamente adaptadas para Unity e Unreal Engine. Entenda por que depender do onTrimMemory baseado em Java pode causar impasses no Unity e como usar callbacks nativos do ciclo de vida. Você também vai descobrir otimizações importantes no nível do recurso, como usar a compactação de textura ASTC 8x8 e configurar o descarregamento de recursos, para manter o jogo funcionando sem problemas em todas as categorias de hardware.
Para mais informações, consulte Reduzir o uso de memória.