Limitador de memória

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 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_ACTIVITYEm cache
CACHED_ACTIVITY_CLIENTEm cache
CACHED_RECENTEm cache
CACHED_EMPTYEm 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 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:

  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.