O Android 17 e versões mais recentes são compatíveis com o Memory Limiter, um serviço do sistema que monitora e limita o uso da memória de processos de aplicativos usando o cgroup v2 do Linux. O Memory Limiter impede que apps individuais consumam memória excessiva do sistema, o que reduz a pressão da memória em todo o sistema e evita o encerramento agressivo por falta de memória (OOM) de processos críticos.
Mecanismo
O Memory Limiter se integra ao serviço Activity Manager (AMS, na sigla em inglês) para rastrear eventos de ciclo de vida do processo e mudanças de estado. O Memory Limiter impõe limites de memória usando o sistema de arquivos cgroup v2 do kernel do Linux.
Para usar o Memory Limiter, o kernel do dispositivo precisa oferecer suporte a cgroup v2 e ao controlador
memory. O serviço depende especificamente dos seguintes atributos:
memory.high- Um limite flexível. Quando excedido, o processo é limitado e o kernel tenta recuperar a memória dele.
memory.swap.max- Limita a quantidade de espaço de troca que o processo pode usar.
Impacto nos apps
Os apps que não excedem os limites de memória não são afetados pelo Memory Limiter.
Quando um app ultrapassa o limite de memory.high, o kernel desaloca a memória
baseada em arquivo do app e troca a memória anônima para manter o app dentro do
limite. Como resultado do despejo e da troca, o app pode ficar mais lento.
No extremo, se o app continuar alocando memória anônima e o dispositivo ficar sem espaço de troca, o app poderá não alocar memória e, como resultado, provavelmente vai falhar.
Monitoramento de processos
Por padrão, o Memory Limiter monitora processos de apps (UID >= 10000). Os processos do sistema geralmente são isentos para ajudar a verificar a estabilidade do sistema principal.
O Memory Limiter atribui limites de memória com base no estado do processo:
Os processos visíveis estão em um estado em que podem mostrar uma interface ao usuário. Ao mostrar uma interface, espera-se que um processo use um conjunto de trabalho de RAM maior, então ele recebe um limite de memória mais generoso.
Os processos não visíveis estão em um estado em que estão trabalhando ativamente, mas não desenhando uma interface. Eles usam memória para realizar esse trabalho, mas menos do que precisam ao mostrar uma interface, então recebem um limite mais restritivo.
A tabela a seguir mapeia estados de processo específicos para limites de memória:
| Estado do processo | Limite de memória |
|---|---|
PERSISTENT | Irrestrito |
PERSISTENT_UI | Irrestrito |
TOP | Visível |
BOUND_TOP | Visível |
FOREGROUND_SERVICE | Não visível |
BOUND_FOREGROUND_SERVICE | Não visível |
IMPORTANT_FOREGROUND | Visível |
IMPORTANT_BACKGROUND | Não visível |
TRANSIENT_BACKGROUND | Não visível |
BACKUP | Não visível |
SERVICE | Não visível |
RECEIVER | Não visível |
TOP_SLEEPING | Visível |
HEAVY_WEIGHT | Não visível |
HOME | Não visível |
LAST_ACTIVITY | Não visível |
CACHED_ACTIVITY | Em cache |
CACHED_ACTIVITY_CLIENT | Em cache |
CACHED_RECENT | Em cache |
CACHED_EMPTY | Em cache |
No estado em cache, os processos são congelados e depois recuperados ao máximo.
Quando um processo excede o limite de memory.high atribuído, o Memory Limiter
detecta o evento e pode acionar ações de depuração, como capturar um perfil
de memória ou registrar uma anomalia em statsd.
Configuração
Configure o limitador de memória usando um arquivo XML localizado na partição vendor. A configuração permite ajustar os limites absolutos de memória com base nas restrições específicas do dispositivo.
Caminho do arquivo:
/vendor/etc/memory-limiter-config.xmlConfiguração padrão:se o arquivo de configuração não for encontrado ou se ele estiver ilegível ou inválido, o limitador de memória será desativado.
Formato XML
O arquivo de configuração segue o esquema definido em
memory-limiter-config.xsd. O arquivo permite definir vários conjuntos de limites, e o serviço escolhe a melhor correspondência com base na RAM disponível do dispositivo. Todos os valores de memória são definidos em unidades de mebibytes (MiB).
<MemoryLimiterConfig>
<version>1</version>
<configList>
<limitSet>
<!-- Limits for a phone with at least 14G of ram: 8G/4G/4G/4G -->
<minimumRequiredMemTotal>14336</minimumRequiredMemTotal>
<memVisible>8192</memVisible>
<memNotVisible>4096</memNotVisible>
<swapVisible>4096</swapVisible>
<swapNotVisible>4096</swapNotVisible>
</limitSet>
</configList>
</MemoryLimiterConfig>
version- Um número inteiro positivo que identifica a versão da configuração. Precisa ser 1.
minimumRequiredMemTotal- A memória mínima do sistema disponível necessária para que esse limite definido seja válido.
memVisible- O limite de memória (
memory.high) permitido para processos visíveis. memNotVisible- O limite de memória (
memory.high) permitido para processos não visíveis. swapVisible- O limite de troca (
memory.swap.max) permitido para processos visíveis. swapNotVisible- O limite de troca (
memory.swap.max) permitido para processos não visíveis.
Diretrizes de limite de memória do dispositivo
Ao configurar limites de memória para seu dispositivo, considere as seguintes diretrizes:
Adaptar limites às capacidades de hardware:os OEMs de dispositivos podem definir limites adaptados às capacidades de hardware do dispositivo. O Android recomenda os seguintes intervalos:
- Processos visíveis:pelo menos 1/2 e no máximo 2/3 do total de RAM física.
- Processos não visíveis:1/4 a 1/3 da RAM física total. Os OEMs podem fazer determinações diferentes com base nas funcionalidades e nos casos de uso do dispositivo.
Sem API de tempo de execução para apps:desde o Android 17 (SDK 37), os apps não têm uma API para consultar os limites de memória no tempo de execução. Os OEMs precisam levar isso em consideração e evitar definir limites muito baixos, garantindo que os apps não atinjam os limites durante casos de uso razoáveis.
Configuração universal:os limites se aplicam a todos os processos de apps no dispositivo, incluindo os pré-instalados. Não há uma lista de permissão para isentar determinados apps desses limites.
Modificar configuração
Para mudar os limites em todo o sistema, siga estas etapas:
- Modificar
/vendor/etc/memory-limiter-config.xml. - Reinicie o dispositivo ou o
system_serverpara que as mudanças entrem em vigor.
Comandos do shell
O comando am memory-limiter permite que você e os desenvolvedores interajam com o
serviço no tempo de execução para desenvolvimento e teste:
am memory-limiter <SUB-COMMAND>status
O subcomando status informa o status operacional do Memory Limiter:
adb shell am memory-limiter statusExemplo de saída:
Memory limiter
enabled monitoring=true ignored=none
visibleMem=1948MB visibleSwap=974MB
notVisibleMem=974MB notVisibleSwap=487MB
started=36 watched=36 watch-failed=0
events=0 processes=36 process-hwm=36
Os principais campos na saída incluem:
monitoring- Indica se o limitador está monitorando ativamente os processos.
visibleMemenotVisibleMem- Indique os limites absolutos de memória calculados para cada estado.
events- O número de vezes que um processo excedeu o limite.
processes- O número de processos monitorados.
ignorar
O subcomando ignore exclui temporariamente um UID ou todos os processos de serem
limitados. Essa ação é útil para testes de desempenho ou quando você permite que um
app específico exceda os limites.
adb shell am memory-limiter ignore 10087 // Ignore a specific UIDadb shell am memory-limiter ignore all // Ignore all processes (effectively disables limiting)adb shell am memory-limiter ignore none // Resume normal operation
manual
O subcomando manual substitui os limites calculados de um processo específico (por ID do processo ou PID) com um valor absoluto personalizado em megabytes (MB):
adb shell am memory-limiter manual 1234 1024 // Set a 1024 MB limit for PID 1234adb shell am memory-limiter manual 1234 none // Remove the manual override for PID 1234
As substituições manuais se aplicam apenas ao ciclo de vida do processo. Se o processo for reiniciado, ele voltará aos limites padrão com base no estado.