O Google tem o compromisso de promover a igualdade racial para as comunidades negras. Saiba como.

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 (link em inglês).

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 paginados quando necessário. Exemplos de dados estáticos incluem: código Dalvik (colocando-o em um .odex para mapeamento direto), recursos do app (projetando a tabela de recursos para ser uma estrutura que pode ser mapeada e alinhando as entradas zip do APK) e elementos tradicionais do projeto, como código nativo em .so arquivos.
  • 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 a pilha do app, o Android calcula um valor chamado PSS (Tamanho do conjunto proporcional, em português), que considera páginas limpas e sujas que são compartilhadas com outros processos, mas apenas em uma quantidade proporcional ao número de apps compartilhados pela RAM. Esse total de PSS é o que o sistema considera como sua 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 multitarefa funcional, o Android define um limite rígido para o tamanho de heap de cada app. O limite exato de tamanho de heap varia entre os dispositivos com base na quantidade de RAM disponível para o dispositivo. Se o app atingir a capacidade de 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. É possível consultar o sistema para determinar esse número chamando 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 em um cache 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 serviços de primeiro plano, como reprodução de músicas. 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 o app tiver um processo armazenado em cache e reter recursos que não são necessários no momento, mesmo que o usuário não os use, o desempenho geral do sistema será afetado. Como o sistema fica sem recursos, como memória, ele encerra processos no cache. 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: quanto menos memória seu app consumir enquanto estiver no cache, melhores serão suas chances de não serem encerrados e poderem ser retomados rapidamente. No entanto, dependendo dos requisitos do sistema instantâneo, é possível encerrar processos em cache a qualquer momento, independentemente da utilização de recursos.

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 Processos e linhas de execução.