A Visualização do desenvolvedor para Android 11 já está disponível. Teste e compartilhe seu feedback.

Visão geral do gerenciamento de memória

O Android Runtime (ART) e a máquina virtual Dalvik usam paginação e mapeamento em memória (mmapping) para gerenciar a memória. Isso significa que qualquer memória que um app modifique, seja alocando novos objetos ou tocando em páginas mapeadas em memória, continua residindo na RAM e não pode ser despaginada. A única maneira de liberar a memória de um app é liberar as referências de objetos mantidas por ele, disponibilizando a memória para o coletor de lixo. Isso ocorre com uma exceção: todos os arquivos mapeados em memória sem modificação, como código, podem ser despaginados da RAM caso o sistema queira usar essa memória em outro lugar.

Esta página explica como o Android gerencia processos de apps e alocação de memória. Para saber mais sobre como gerenciar memória de forma mais eficiente no seu app, consulte Gerenciar a memória do seu app.

Coleta de lixo

Um ambiente de memória gerenciada, como o ART ou a máquina virtual Dalvik, monitora cada alocação de memória. Depois que ele determina que uma parte da memória não está mais sendo usada pelo programa, ela é liberada de volta para o heap, sem qualquer intervenção do programador. O mecanismo para recuperar memória não utilizada em um ambiente de memória gerenciada é conhecido como coleta de lixo. A coleta de lixo tem dois objetivos: localizar objetos de dados em um programa que não pode ser acessado no futuro e recuperar os recursos usados por esses objetos.

O heap de memória do Android é geracional, o que significa que há diferentes intervalos de alocações rastreados com base na vida útil esperada e no tamanho de um objeto alocado. Por exemplo, objetos alocados recentemente pertencem à geração jovem. Quando um objeto permanece ativo por tempo suficiente, ele pode ser promovido a uma geração mais antiga, seguida por uma geração permanente.

Cada geração de heap tem o próprio limite máximo dedicado com relação ao volume de memória que os objetos podem ocupar. Sempre que uma geração começa a ser preenchida, o sistema executa um evento de coleta de lixo para tentar liberar memória. A duração da coleta de lixo depende da geração de objetos que ela está coletando e da quantidade de objetos ativos em cada geração.

Mesmo que a coleta de lixo possa ser bastante rápida, ela ainda pode afetar o desempenho do app. Em geral, não é possível controlar quando um evento de coleta de lixo ocorre no seu código. O sistema tem um conjunto de critérios para determinar quando a coleta de lixo deve ser feita. Quando os critérios são atendidos, o sistema interrompe a execução do processo e inicia a coleta de lixo. Se a coleta de lixo ocorrer no meio de um loop de processamento intensivo, como uma animação ou durante a reprodução de uma música, o tempo de processamento poderá aumentar. Esse aumento pode fazer a execução de código no app ultrapassar o limite recomendado de 16 ms para a renderização eficiente e uniforme de frames.

Além disso, o fluxo de código pode realizar tipos de trabalho que forçam os eventos de coleta de lixo a ocorrer com mais frequência ou que os fazem durar mais tempo que o normal. Por exemplo, se você alocar vários objetos na parte mais interna de um loop "for" durante cada frame de uma animação de mistura Alfa, poluirá o heap da memória com muitos objetos. Nessa circunstância, o coletor de lixo executa vários eventos de coleta de lixo e pode prejudicar o desempenho do app.

Para ver informações mais gerais sobre a coleta de lixo, consulte Coleta de lixo.

Compartilhar memória

Para ajustar tudo o que é necessário na RAM, o Android tenta compartilhar páginas da RAM entre os processos. Isso pode ser feito das seguintes maneiras:

  • Cada processo do app é bifurcado a partir de um processo existente denominado "Zigoto". O processo Zigoto começa quando o sistema inicializa e carrega códigos e recursos comuns do framework, como temas da atividade. Para iniciar um novo processo do app, o sistema bifurca o processo Zigoto e, em seguida, carrega e executa o código do app no novo processo. Essa abordagem permite que a maioria das páginas da RAM alocadas para o código e os recursos do framework seja compartilhada em todos os processos do app.
  • A maioria dos dados estáticos é mapeada em memória para um processo. Essa técnica permite que os dados sejam compartilhados entre processos e que eles sejam paginados quando necessário. Exemplos de dados estáticos incluem: código Dalvik (colocando-o em um arquivo .odex pré-vinculado para mapeamento direto em memória), recursos de app (criando a tabela de recursos como uma estrutura que pode ser mapeada em memória e alinhando as entradas zip do APK) e elementos tradicionais do projeto, como código nativo em arquivos .so.
  • Em muitos lugares, o Android compartilha a mesma RAM dinâmica entre processos por meio de regiões de memória compartilhada explicitamente alocadas (com ashmem ou gralloc). Por exemplo, as superfícies de janela usam memória compartilhada entre o app e o compositor de tela, e os buffers do cursor usam a memória compartilhada entre o provedor de conteúdo e o cliente.

Devido ao uso extensivo de memória compartilhada, é preciso ter cuidado para determinar a quantidade de memória que seu app está usando. As técnicas para determinar corretamente o uso de memória no seu app são discutidas em Investigar o uso de RAM.

Alocar e recuperar memória do app

O heap da Dalvik é restrito a um único intervalo de memória virtual para cada processo do app. Isso define o tamanho lógico do heap, que pode aumentar conforme o necessário, mas apenas até um limite definido pelo sistema para cada app.

O tamanho lógico do heap não é o mesmo que a quantidade de memória física usada pelo heap. Ao inspecionar o heap do seu app, o Android calcula um valor denominado Proportional Set Size (PSS), que diz respeito às páginas limpas e sujas compartilhadas com outros processos, mas apenas em uma quantidade proporcional ao número de apps que compartilham essa RAM. Esse total (PSS) é aquilo que o sistema considera como consumo da memória física. Para saber mais sobre o PSS, consulte o guia Investigar o uso de RAM.

O heap da Dalvik não compacta o tamanho lógico do heap, o que significa que o Android não desfragmenta o heap para fechar espaço. O Android só pode reduzir o tamanho lógico do heap quando há espaço não utilizado no final do heap. No entanto, o sistema ainda pode reduzir a memória física usada pelo heap. Depois da coleta de lixo, a Dalvik percorre o heap e encontra páginas não utilizadas. Em seguida, ele retorna essas páginas ao kernel por meio do madvise. Portanto, as alocações pareadas e as desalocações de blocos grandes devem resultar na recuperação de toda (ou quase toda) a memória física usada. No entanto, a recuperação de memória de pequenas alocações pode ser muito menos eficiente, porque a página usada para uma pequena alocação ainda pode ser compartilhada com algo que ainda não foi liberado.

Restringir memória do app

Para manter um ambiente multitarefas funcional, o Android define um limite rígido para o tamanho de heap de cada app. O limite exato para o tamanho de heap varia entre os dispositivos com base na quantidade de RAM disponível no dispositivo. Se o app tiver atingido a capacidade do heap e tentar alocar mais memória, ele poderá receber um OutOfMemoryError.

Em alguns casos, você pode consultar o sistema para determinar exatamente quanto espaço de heap está disponível no dispositivo atual. Por exemplo, para determinar quantos dados devem ser mantidos em cache. Para fazer essa consulta, chame getMemoryClass(). Esse método retorna um número inteiro indicando o número de megabytes disponíveis para o heap do app.

Alternar entre apps

Quando os usuários alternam entre apps, o Android mantém os apps que não estão em primeiro plano (ou seja, não estão visíveis para o usuário ou estão executando um serviço no primeiro plano, como músicas), em um cache usado há mais tempo (LRU, na sigla em inglês). Por exemplo, quando o usuário abre um app pela primeira vez, um processo é criado para ele. Mas, quando o usuário sai do app, esse processo não é encerrado. O sistema mantém o processo em cache. Se o usuário retornar ao app mais tarde, o sistema reutilizará o processo, tornando a alternância de apps mais rápida.

Se seu app tiver um processo armazenado em cache e retiver uma memória que não é necessária no momento, o app afetará o desempenho geral do sistema mesmo se não estiver em uso. À medida que a memória do sistema diminui, ele encerra processos no cache LRU, começando pelo processo usado há mais tempo. O sistema também considera processos que mantêm a maior parte da memória e pode encerrá-los para liberar a RAM.

Observação: quando o sistema começa a encerrar processos no cache LRU, ele funciona principalmente de baixo para cima. O sistema também considera quais processos consomem mais memória e, consequentemente, oferecem mais ganho de memória quando são encerrados. Quanto menos memória você consumir ao todo na lista de LRUs, melhores serão suas chances de permanecer na lista e retomar o processo rapidamente.

Para saber mais sobre como os processos são armazenados em cache enquanto não são executados no primeiro plano e como o Android decide quais deles podem ser encerrados, consulte o guia Visão geral dos processos e linhas de execução.