O Android 17 e versões mais recentes oferecem suporte ao Memory Limiter, um serviço do sistema que monitora e limita o uso da memória dos 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 de memória em todo o sistema e evita o encerramento agressivo de processos críticos por falta de memória (OOM, na sigla em inglês).
Mecanismo
O Memory Limiter se integra ao Activity Manager Service (AMS) para rastrear eventos do ciclo de vida do processo e mudanças de estado. O Memory Limiter aplica 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 ao 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 remove a memória com suporte a arquivos do app e troca a memória anônima para manter o app dentro do limite. Como resultado da remoção 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á falhar ao alocar memória e, como resultado, provavelmente vai falhar.
Monitoramento de processos
O Memory Limiter monitora os processos de apps (UID >= 10000) por padrão. Os processos do sistema geralmente são isentos para ajudar a verificar a estabilidade principal do sistema.
O Memory Limiter atribui limites de memória com base no estado do processo:
Processos visíveis são perceptíveis para o usuário, como atividades em primeiro plano, serviços em primeiro plano ou outros estados perceptíveis.
Processos não visíveis são processos em segundo plano que não estão interagindo ou visíveis para o usuário.
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 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 no statsd.
Configuração
Configure o Memory Limiter 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 de memória 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 não puder ser lido ou for inválido, o Memory Limiter 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. 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. Esse valor precisa ser 1.
minimumRequiredMemTotal- A memória mínima disponível do sistema necessária para que esse conjunto de limites 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 o dispositivo, considere as seguintes diretrizes:
Limites personalizados para recursos de hardware:os OEMs de dispositivos podem definir limites personalizados para os recursos de hardware do dispositivo. O Android recomenda os seguintes intervalos:
- Processos visíveis:pelo menos 1/2 e no máximo 2/3 da RAM física total.
- Processos não visíveis:de 1/4 a 1/3 da RAM física total. Os OEMs podem fazer determinações diferentes com base nos recursos e casos de uso do dispositivo.
Nenhuma API de execução para apps:a partir do Android 17 (SDK 37), os apps não têm uma API para consultar os limites de memória no momento da execução. Os OEMs precisam considerar isso 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 apps pré-instalados. Não há uma lista de permissões para isentar determinados apps desses limites.
Modificar configuração
Para mudar os limites em todo o sistema, siga estas etapas:
- Modifique
/vendor/etc/memory-limiter-config.xml. - Reinicie o dispositivo ou reinicie
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 momento da 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- Indicam 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 ao permitir 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 para um processo específico (por ID do processo ou PID) por 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 retornará aos limites padrão com base no estado.