O Android 17 e versões mais recentes incluem o 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 do sistema de forma desproporcional, o que reduz a pressão geral da memória e evita encerramentos agressivos de processos críticos em primeiro plano e em cache devido à pouca memória.
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 a hierarquia do cgroup v2 do kernel do Linux.
Para oferecer suporte ao Memory Limiter, o kernel do dispositivo precisa ativar o cgroup v2 e o 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 página de forma proativa desse processo.
memory.swap.max- Limita a quantidade máxima de espaço de troca (como ZRAM) que o processo pode usar.
Impacto nos apps
Os apps que operam dentro dos 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 limpa com suporte a arquivos do app e troca a memória anônima inativa para manter o app dentro do limite. Como resultado dessa remoção de página e atividade de troca, o app pode sofrer limitação temporária de execução, mas continua em execução.
Em situações extremas, como um vazamento de memória descontrolado em que o app continua alocando memória anônima após o espaço de troca ser esgotado, o app não consegue alocar memória e é encerrado.
Monitoramento de processos
O Memory Limiter monitora os processos de apps (UID >= 10000) por padrão. Os processos principais do sistema são isentos para preservar a estabilidade essencial do sistema.
O Memory Limiter atribui limites de memória com base no estado de visibilidade do processo:
Processos visíveis:processos que hospedam uma interface visível ao usuário (como a atividade focada no momento ou uma interação ativa). Como a exibição de uma interface exige um conjunto de trabalho maior para renderização e pipelines gráficos, os processos visíveis recebem um limite de memória mais generoso.
Processos não visíveis:processos que realizam o trabalho em segundo plano sem apresentar uma interface (como serviços em segundo plano ou receptores de transmissão). Como eles não renderizam componentes de interface, operam com um limite mais restritivo.
A tabela a seguir mapeia estados de processo específicos para classificações de limite 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 diagnóstico, como capturar um perfil de memória ou registrar uma anomalia no statsd.
Configuração
No Android 17 e versões mais recentes, o Memory Limiter fornece um arquivo de configuração padrão da plataforma na partição do sistema, oferecendo suporte a substituições opcionais específicas do dispositivo na partição vendor:
- Configuração padrão do sistema:
/system/etc/memory-limiter-config.xml - Substituição do fornecedor (opcional) :
/vendor/etc/memory-limiter-config.xml
O Memory Limiter verifica primeiro o arquivo /vendor/etc/memory-limiter-config.xml.
Se presente, ele usa a configuração do fornecedor. Caso contrário, ele volta para a configuração padrão do sistema em /system/etc/memory-limiter-config.xml.
Formato XML
O arquivo de configuração segue o esquema definido em memory-limiter-config.xsd. O arquivo define vários conjuntos de limites ordenados pela memória disponível. O serviço seleciona o conjunto de limites correspondente mais alto com base na RAM disponível do dispositivo. Todos os valores de memória são especificados em mebibytes (MiB).
<MemoryLimiterConfig>
<version>1</version>
<configList>
<!-- RAM minimums account for MemTotal excluding carve-outs. -->
<limitSet>
<!-- Limits for 16GB RAM device (MemTotal >= 13GiB): 10G/5G/5G/5G -->
<minimumRequiredMemTotal>13312</minimumRequiredMemTotal>
<memVisible>10240</memVisible>
<memNotVisible>5120</memNotVisible>
<swapVisible>5120</swapVisible>
<swapNotVisible>5120</swapNotVisible>
</limitSet>
<limitSet>
<!-- Limits for 12GB RAM device (MemTotal >= 10GiB): 8G/4G/4G/4G -->
<minimumRequiredMemTotal>10240</minimumRequiredMemTotal>
<memVisible>8192</memVisible>
<memNotVisible>4096</memNotVisible>
<swapVisible>4096</swapVisible>
<swapNotVisible>4096</swapNotVisible>
</limitSet>
<limitSet>
<!-- Limits for 8GB RAM device (MemTotal >= 6.5GiB): 5G/3G/3G/3G -->
<minimumRequiredMemTotal>6656</minimumRequiredMemTotal>
<memVisible>5120</memVisible>
<memNotVisible>3072</memNotVisible>
<swapVisible>3072</swapVisible>
<swapNotVisible>3072</swapNotVisible>
</limitSet>
<limitSet>
<!-- Limits for 6GB RAM device (MemTotal >= 4.5GiB): 4G/2G/2G/2G -->
<minimumRequiredMemTotal>4608</minimumRequiredMemTotal>
<memVisible>4096</memVisible>
<memNotVisible>2048</memNotVisible>
<swapVisible>2048</swapVisible>
<swapNotVisible>2048</swapNotVisible>
</limitSet>
<limitSet>
<!-- Limits for 4GB RAM device (MemTotal >= 3GiB): 2G/1G/1G/1G -->
<minimumRequiredMemTotal>3072</minimumRequiredMemTotal>
<memVisible>2048</memVisible>
<memNotVisible>1024</memNotVisible>
<swapVisible>1024</swapVisible>
<swapNotVisible>1024</swapNotVisible>
</limitSet>
</configList>
</MemoryLimiterConfig>
version- Um número inteiro positivo que identifica a versão da configuração. Esse valor precisa ser
1. minimumRequiredMemTotalO
MemTotalmínimo do sistema (em MiB) necessário para que esse conjunto de limites seja aplicado. O serviço avalia isso em relação à memória total disponível informada pelo kernel em/proc/meminfo.Entender as reservas de memória:o valor
MemTotaldo kernel reflete a DRAM física acessível ao sistema operacional após a dedução das reservas de memória de hardware e firmware. As reservas reservam RAM para componentes de hardware especializados, como GPU, modem de banda base, ISP da câmera, ambientes de execução seguros e hipervisores, geralmente consumindo entre 500 MiB e mais de 1 GiB de memória física.Como
MemTotalé menor que a DRAM física anunciada do dispositivo,minimumRequiredMemTotalos limites incluem uma margem para essas reservas. Por exemplo, uma configuração direcionada a dispositivos de 16 GB especifica umminimumRequiredMemTotalde13312(13 GiB) em vez de16384(16 GiB), e um nível para dispositivos de 12 GB especifica10240(10 GiB). Isso garante que cada dispositivo corresponda ao nível pretendido, independentemente das variações de reserva no nível da placa.memVisibleO limite de memória flexível (
memory.high) em MiB aplicado a processos visíveis.memNotVisibleO limite de memória flexível (
memory.high) em MiB aplicado a processos não visíveis.swapVisibleO limite de espaço de troca (
memory.swap.max) em MiB aplicado a processos visíveis.swapNotVisibleO limite de espaço de troca (
memory.swap.max) em MiB aplicado a processos não visíveis.
Limites padrão da plataforma
No Android 17 e versões mais recentes, a plataforma especifica limites padrão de memória e troca em /system/etc/memory-limiter-config.xml em níveis comuns de RAM física:
| RAM física de destino |
MemTotal mínimo necessário (minimumRequiredMemTotal) |
Limite de memória visível (memVisible) |
Limite de memória não visível (memNotVisible) |
Limite de troca visível (swapVisible) |
Limite de troca não visível (swapNotVisible) |
|---|---|---|---|---|---|
| 16 GB | 13.312 MiB (13 GiB) | 10.240 MiB (10 GiB) | 5.120 MiB (5 GiB) | 5.120 MiB (5 GiB) | 5.120 MiB (5 GiB) |
| 12 GB | 10.240 MiB (10 GiB) | 8.192 MiB (8 GiB) | 4.096 MiB (4 GiB) | 4.096 MiB (4 GiB) | 4.096 MiB (4 GiB) |
| 8 GB | 6.656 MiB (6,5 GiB) | 5.120 MiB (5 GiB) | 3.072 MiB (3 GiB) | 3.072 MiB (3 GiB) | 3.072 MiB (3 GiB) |
| 6 GB | 4.608 MiB (4,5 GiB) | 4.096 MiB (4 GiB) | 2.048 MiB (2 GiB) | 2.048 MiB (2 GiB) | 2.048 MiB (2 GiB) |
| 4 GB | 3.072 MiB (3 GiB) | 2.048 MiB (2 GiB) | 1.024 MiB (1 GiB) | 1.024 MiB (1 GiB) | 1.024 MiB (1 GiB) |
Princípios de limite de memória
A configuração do Memory Limiter foi projetada com base nos seguintes princípios da plataforma:
Consistência do ecossistema e compatibilidade de apps:limites de memória consistentes em todos os dispositivos garantem a performance previsível do app em todo o ecossistema Android. Os apps são desenvolvidos e testados em relação às expectativas de memória padrão. A manutenção de limites uniformes da plataforma evita a limitação inesperada ou encerramentos prematuros.
Alocação proporcional de recursos:os limites da plataforma são calibrados de acordo com a capacidade de RAM física:
- Processos visíveis:aproximadamente 1/2 a 2/3 da RAM física total alocada para oferecer suporte a cargas de trabalho ativas de interface, composição e renderização.
- Processos não visíveis:aproximadamente 1/4 a 1/3 da RAM física total alocada para tarefas em segundo plano.
Aplicabilidade universal:os limites são aplicados uniformemente a todos os processos de aplicativos no dispositivo (UID >= 10000), incluindo apps do sistema e OEM pré-instalados. O Memory Limiter não oferece suporte a listas de permissões para isentar apps específicos, garantindo uma governança de memória justa em todos os apps.
Ausência de APIs de consulta de tempo de execução:no Android 17 e versões mais recentes, os apps não podem consultar programaticamente os limites de memória atribuídos no tempo de execução. Os limites padrão da plataforma são definidos com margens generosas para que os apps bem-comportados operem sem restrições durante o uso normal.
Recuperação do kernel e dinâmica de troca:quando um processo de app se aproxima do limite de
memory.high, o kernel do Linux direciona a recuperação de memória especificamente para esse cgroup de processo. Isso inclui descartar páginas inativas com suporte a arquivos e trocar páginas anônimas frias para ZRAM, mantendo a fluidez do sistema sem impor pressão global de memória.
Comandos do shell
O comando am memory-limiter permite que desenvolvedores e integradores de sistemas interajam com o serviço no tempo de execução para desenvolvimento, testes e depuração:
am memory-limiter <SUB-COMMAND>status
O subcomando status informa o status operacional e as métricas ativas 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 Memory Limiter está monitorando ativamente os processos.
visibleMemenotVisibleMem- Os limites de memória absolutos calculados (
memory.high) atualmente aplicados a cada estado de visibilidade. visibleSwapenotVisibleSwap- Os limites de troca absolutos calculados (
memory.swap.max) atualmente aplicados a cada estado de visibilidade. events- O número de vezes que um processo ultrapassou o limite atribuído.
processes- O número atual de processos monitorados.
ignorar
O subcomando ignore exclui temporariamente um UID específico ou todos os processos da limitação de memória. Isso é útil para benchmarking de performance, testes de estresse ou diagnóstico de comportamento de memória:
# Ignore a specific UID adb shell am memory-limiter ignore 10087# Ignore all processes (temporarily disables limiting) adb shell am memory-limiter ignore all# Resume normal limiting operation adb shell am memory-limiter ignore none
manual
O subcomando manual substitui os limites calculados de um processo específico pelo ID do processo (PID) com um valor absoluto personalizado em bytes. O valor precisa ser um número inteiro, mas pode incluir o sufixo MB para indicar MiB ou o sufixo GB para indicar GiB:
# Set a 1GiB limit for PID 1234 adb shell am memory-limiter manual 1234 1073741824# Set a 1GiB limit for PID 1234 adb shell am memory-limiter manual 1234 1024MB# Set a 1GiB limit for PID 1234 adb shell am memory-limiter manual 1234 1GB# Remove the manual override for PID 1234 adb shell am memory-limiter manual 1234 none
As substituições manuais se aplicam apenas à vida útil dessa instância de processo específica. Se o processo for reiniciado, ele retornará aos limites padrão com base no estado.
Uma substituição manual não pode exceder os limites físicos do sistema.