Limitador de memória

O Android 17 e versões mais recentes oferecem suporte ao limitador de memória, que é 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 limitador de memória 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) para rastrear eventos de 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 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:

  • 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 de instabilidade.

  • Os processos não visíveis são executados em segundo plano e não interagem com o usuário nem ficam visíveis para ele.

A tabela a seguir mapeia estados de processo específicos para limites de memória:

Estado do processoLimite de memória
PERSISTENTIrrestrito
PERSISTENT_UIIrrestrito
TOPVisível
BOUND_TOPVisível
FOREGROUND_SERVICENão visível
BOUND_FOREGROUND_SERVICENão visível
IMPORTANT_FOREGROUNDVisível
IMPORTANT_BACKGROUNDNão visível
TRANSIENT_BACKGROUNDNão visível
BACKUPNão visível
SERVICENão visível
RECEIVERNão visível
TOP_SLEEPINGVisível
HEAVY_WEIGHTNão visível
HOMENão visível
LAST_ACTIVITYNão visível
CACHED_ACTIVITYArmazenado em cache
CACHED_ACTIVITY_CLIENTArmazenado em cache
CACHED_RECENTArmazenado em cache
CACHED_EMPTYArmazenado 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.xml

  • Configuraçã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 disponível do sistema necessária para que esse limite 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.

Modificar configuração

Para mudar os limites em todo o sistema, siga estas etapas:

  1. Modificar /vendor/etc/memory-limiter-config.xml.
  2. Reinicie o dispositivo ou o system_server para 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 status

Exemplo 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.
visibleMem e notVisibleMem
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 UID
adb 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 1234
adb 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.