A plataforma Android está baseada no princípio de que memória livre é memória desperdiçada. Ela tenta usar toda a memória disponível em todos os momentos. Por exemplo, o sistema mantém os apps na memória depois de terem sido fechados para que o usuário possa voltar a eles rapidamente. Por esse motivo, os dispositivos Android geralmente são executados com pouca memória livre. O gerenciamento é fundamental para uma alocação adequada da memória entre processos importantes do sistema e muitos aplicativos do usuário.
Esta página discute os conceitos básicos sobre como o Android aloca memória para o sistema e para aplicativos do usuário. Ela também explica como o sistema operacional reage a situações de pouca memória.
Tipos de memória
Os dispositivos Android contêm três tipos de memória: RAM, zRAM e de armazenamento. Observe que a CPU e a GPU acessam a mesma RAM.
Figura 1. Tipos de memória: RAM, zRAM e de armazenamento.
RAM é o tipo de memória mais rápido, mas geralmente é de tamanho limitado. Os dispositivos mais modernos normalmente têm mais memória RAM.
zRAM é uma partição da RAM usada para espaço de troca. Tudo é compactado quando colocado na zRAM e descompactado quando copiado dela. Essa parte da RAM aumenta ou diminui de tamanho conforme as páginas são movidas para a memória zRAM ou retiradas dela. Os fabricantes de dispositivos podem definir o tamanho máximo.
O armazenamento contém todos os dados persistentes, por exemplo, o sistema de arquivos e o código de objeto incluído para todos os apps, bibliotecas e a plataforma. O armazenamento tem uma capacidade muito maior que a dos outros dois tipos de memória. No Android, o armazenamento não é usado para espaço de troca como em outras implementações do Linux, porque a gravação frequente pode causar desgaste nessa memória e reduzir a vida útil da mídia de armazenamento.
Páginas de memória
A RAM é dividida em páginas. Normalmente, cada página tem 4 KB de memória.
As páginas são consideradas livres ou usadas. As páginas livres são RAM não usada. Páginas usadas são RAM em uso no sistema e são divididas nas categorias abaixo:
- Em cache: memória usada por um arquivo no armazenamento, por exemplo, arquivos de código ou
mapeados na memória. Há dois tipos de memória em cache:
- Particular: pertence a um processo e não é compartilhada.
- Limpa: cópia não modificada de um arquivo no armazenamento. Pode ser excluída por
kswapd
para aumentar a memória livre. - Suja: cópia modificada do arquivo no armazenamento. Pode ser movida ou
compactada na zRAM por
kswapd
para aumentar a memória livre.
- Limpa: cópia não modificada de um arquivo no armazenamento. Pode ser excluída por
- Compartilhada: usada por vários processos.
- Limpa: cópia não modificada do arquivo no armazenamento. Pode ser excluída por
kswapd
para aumentar a memória livre. - Suja: cópia modificada do arquivo no armazenamento. Permite que mudanças sejam
gravadas novamente no arquivo em armazenamento para aumentar a memória livre com
kswapd
ou usando explicitamentemsync()
oumunmap()
.
- Limpa: cópia não modificada do arquivo no armazenamento. Pode ser excluída por
- Particular: pertence a um processo e não é compartilhada.
- Anônima: memória não protegida por um arquivo no armazenamento (por exemplo,
alocada por
mmap()
com a flagMAP_ANONYMOUS
definida).- Suja: pode ser movida/compactada na zRAM por
kswapd
para aumentar a memória livre.
- Suja: pode ser movida/compactada na zRAM por
As proporções de páginas livres e usadas variam com o tempo, uma vez que o sistema gerencia ativamente a RAM. Os conceitos introduzidos nesta seção são essenciais para gerenciar situações de pouca memória. A próxima seção explica esses conceitos em mais detalhes.
Gerenciamento de pouca memória
O Android tem dois mecanismos principais para lidar com situações de pouca memória: o kernel swap daemon e o low-memory killer.
kernel swap daemon
O kernel swap daemon (kswapd
) faz parte do kernel do Linux e converte a memória usada
em memória livre. O daemon é ativado quando a memória livre no
dispositivo fica baixa. O kernel do Linux mantém limites mínimos e máximos de memória livre.
Quando a memória livre fica abaixo do limite mínimo, o kswapd
começa a liberar
memória. Quando a memória livre atinge o limite máximo, o kswapd
interrompe
a liberação de memória.
O kswapd
pode excluir páginas limpas para liberar memória, porque elas têm um backup no
armazenamento e não foram modificadas. Se um processo tentar acessar uma página limpa
que foi excluída, o sistema copia a página do armazenamento para a RAM. Essa
operação é conhecida como paginação por demanda.
Figura 2. Exclusão de uma página limpa com backup no armazenamento
O kswapd
pode mover páginas sujas particulares e anônimas em cache para a zRAM,
onde são compactadas. Isso libera memória na RAM (páginas
livres). Se um processo tenta tocar em uma página suja na zRAM, a página é
descompactada e movida de volta para a RAM. Se o processo associado a uma
página compactada é encerrado, a página é excluída da zRAM.
Se a quantidade de memória livre ficar abaixo de um determinado limite, o sistema começa a encerrar processos.
Figura 3. Página suja movida para a zRAM e compactada.
Low-memory killer
Muitas vezes, o kswapd
não libera memória suficiente para o sistema. Nesse caso, o
sistema usa
onTrimMemory()
para notificar o app de que a memória está acabando e que ele deve reduzir as
alocações. Se isso não for suficiente, o kernel vai começar a encerrar processos para
liberar memória. Ele usa o low-memory killer (LMK) para fazer isso.
Para decidir qual processo encerrar, o LMK usa uma pontuação de "memória insuficiente" conhecida como
oom_adj_score
para priorizar os processos em execução. Os processos com maior pontuação são encerrados
primeiro. Apps em segundo plano são os primeiros a serem encerrados, e os processos do sistema
são os últimos. A tabela abaixo lista as categorias de pontuação do LMK, da
maior para a menor. Os itens da categoria com maior pontuação na primeira linha são encerrados
primeiro:
Figura 4. Processos do Android com pontuações mais altas na parte de cima, e com pontuações mais baixas na parte de baixo.
Estas são as descrições das várias categorias apresentadas na tabela acima:
Apps em segundo plano: apps executados anteriormente e que não estão ativos no momento. O LMK encerra primeiro os apps em segundo plano, começando por aquele com a
oom_adj_score
mais alta.App anterior: o app em segundo plano usado mais recentemente. O app anterior tem maior prioridade (pontuação mais baixa) que os outros apps em segundo plano, porque é mais provável que o usuário mude para ele do que para outros apps em segundo plano.
App de início: é o app da tela de início. O encerramento desse app faz o plano de fundo desaparecer.
Serviços: são iniciados por aplicativos e podem incluir a sincronização ou o envio para a nuvem.
Apps perceptíveis: apps que não estão no primeiro plano e que são perceptíveis para o usuário de alguma forma, como executar um processo de pesquisa que mostra uma IU pequena ou ouvir música.
App no primeiro plano: o app usado no momento. O encerramento do app no primeiro plano é semelhante a uma falha do aplicativo que pode indicar ao usuário que algo deu errado no dispositivo.
Persistentes (serviços): são os serviços principais do dispositivo, como telefonia e Wi-Fi.
Sistema: processos do sistema. À medida que esses processos são encerrados, o smartphone pode parecer reiniciar.
Nativo: processos de nível muito baixo usados pelo sistema (por exemplo,
kswapd
).
Os fabricantes de dispositivos podem mudar o comportamento do LMK.
Calcular a ocupação da memória
O kernel rastreia todas as páginas de memória no sistema.
Figura 5. Páginas usadas por diferentes processos.
Ao determinar a quantidade de memória usada por um app, o sistema precisa considerar as páginas compartilhadas. Os apps que acessam o mesmo serviço ou biblioteca vão compartilhar páginas de memória. Por exemplo, o Google Play Services e um app de jogo podem compartilhar um serviço de localização. Isso dificulta determinar a quantidade de memória que pertence ao serviço em geral em comparação com cada aplicativo.
Figura 6. Páginas compartilhadas por dois apps (no meio)
Para determinar a ocupação da memória relacionada a um aplicativo, qualquer uma das métricas abaixo pode ser usada:
- Resident Set Size (RSS): número de páginas compartilhadas e não compartilhadas usadas pelo app.
- Proporcional Set Size (PSS): número de páginas não compartilhadas usadas pelo app e uma distribuição uniforme das páginas compartilhadas. Por exemplo, se três processos compartilham 3 MB, cada um deles recebe 1 MB em PSS.
- Unique Set Size (USS): número de páginas não compartilhadas usadas pelo app (páginas compartilhadas não estão incluídas).
O PSS é útil para o sistema operacional quando ele quer saber a quantidade de memória usada por todos os processos, já que as páginas não são contadas várias vezes. O PSS leva muito tempo para ser calculado, porque o sistema precisa determinar quais páginas são compartilhadas e por quantos processos. O RSS não faz distinção entre páginas compartilhadas e não compartilhadas (o que acelera o cálculo) e é mais indicado para rastrear mudanças na alocação de memória.
Outros recursos
- Visão geral do gerenciamento de memória
- Processos e ciclo de vida do app
- Como funciona o uso da memória no Android: apresentação do Google I/O (vídeo em inglês)
- Memória e jogos do Android: apresentação do Google I/O (vídeo em inglês)
- Android Low Memory Killer Daemon (em inglês)
Recomendados para você
- Observação: o texto do link aparece quando o JavaScript está desativado.
- Tempo de inicialização do app