Entender e resolver problemas de memória do Android

A memória é um recurso essencial e finito em dispositivos Android. Como desenvolvedor da plataforma Android ou OEM, entender como a memória funciona em todos os níveis, do ambiente de execução do Java ao kernel do Linux, é essencial para criar um sistema estável e de alto desempenho.

Este guia oferece uma visão geral abrangente da arquitetura de memória do Android, das ferramentas disponíveis para análise e de exercícios práticos para ajudar você a identificar e resolver problemas relacionados à memória.

Objetivos

Depois de concluir este guia, você vai conseguir:

  • Explicar os conceitos fundamentais da memória do Android, incluindo o modelo de processo Zygote e os arquivos mapeados na memória.
  • Diferenciar entre tamanho do conjunto residente (RSS), tamanho do conjunto proporcional (PSS) e tamanho do conjunto exclusivo (USS).
  • Use ferramentas de linha de comando padrão, como dumpsys meminfo e showmap, para avaliar rapidamente o uso da memória.
  • Capture e analise heap dumps do Java usando o AHAT para encontrar vazamentos e alocações excessivas.
  • Use o Perfetto e o heapprofd para analisar a memória Java e nativa em todo o sistema.
  • Entenda os indicadores de pressão de memória em todo o sistema, como as informações de pouca pressão (PSI, na sigla em inglês), e monitore a atividade do Low Memory Killer (LMK).
  • Observe a atividade de recuperação no nível do kernel, incluindo a troca de ZRAM e a remoção do cache de páginas.

Estrutura do guia

Este guia está organizado nas seções a seguir, que devem ser lidas sequencialmente:

  1. Conceitos fundamentais: os princípios técnicos básicos da memória do Android.
  2. Ferramentas de avaliação rápida: use meminfo, showmap e procstats para uma triagem rápida.
  3. Análise da memória Java: Investigações detalhadas do heap do Java com o AHAT.
  4. Bitmaps e memória: entender como as imagens afetam a RAM e usar ferramentas para criar perfis delas.
  5. Análise de memória nativa: criação de perfil de memória C/C++ com o Perfetto e o heapprofd.
  6. WebView e memória: entenda a arquitetura de vários processos e o consumo de memória de WebViews.
  7. O código do app é memória: entenda como o carregamento de classes e o dex bloat afetam a RAM.
  8. Linhas de execução e memória: análise do custo de pilhas de linhas de execução nativas.
  9. Localidade e desempenho da memória: entender como os padrões de acesso a dados afetam o desempenho da CPU.
  10. Vinculações de serviço e estados de processo: entenda como as dependências entre processos afetam as pontuações de OOM.
  11. Solução de problemas em todo o sistema: monitoramento de PSI, LMK, troca de ZRAM e remoção de cache de página.
  12. Recuperação de memória: remoção e troca: entenda o memcg, a cobrança e a recuperação proativa com memory.reclaim.
  13. Interação entre kswapd e lmkd: entenda como o kswapd e o lmkd funcionam juntos sob pressão.
  14. Retornar páginas de memória ao SO: use madvise e posix_fadvise para retornar páginas de heap e de cache de arquivos não utilizadas ao kernel.
  15. Manter o trabalho em segundo plano ativo com um processo enxuto: mover serviços e workers em segundo plano para um processo dedicado para que a memória de interface e gráficos não seja mantida em prioridade elevada.

Pré-requisitos e ambiente

Este guia apresenta fluxos de trabalho de referência, dados de comparativo de mercado e estudos de caso que demonstram como diagnosticar e resolver problemas de memória do Android.

  • Ferramentas de análise: as técnicas neste guia usam principalmente ferramentas padrão de desempenho do Android, incluindo Perfetto, dumpsys meminfo, showmap e AHAT.
  • Privilégios de raiz: alguns fluxos de trabalho de diagnóstico no nível do sistema, como inspecionar a pressão de memória do kernel ou os contadores de PMU de hardware, exigem privilégios de raiz elevados (adb root) em um dispositivo userdebug ou build de emulador.

Comece aqui: conceitos fundamentais