Diagnosticando Swap Thrashing no Linux: Quando a Pressao de Memoria Causa Lentidao

Diagnosticando Swap Thrashing no Linux: Quando a Pressao de Memoria Causa Lentidao

O que e Swap Thrashing?

Conclusao: A memoria fisica esta esgotada, entao o kernel constantemente expulsa paginas para o swap (disco) e as le de volta. A CPU fica quase ociosa enquanto o I/O de disco satura, e todo o sistema fica extremamente lento. A causa raiz e "memoria insuficiente", mas o sintoma parece "o disco esta lento" ou "apenas o load average esta alto".

Quando a memoria fica escassa, o Linux move paginas raramente usadas para a area de swap (swap-out) para liberar RAM. Isso sozinho e normal. O problema comeca quando uma pagina expulsa e necessaria novamente quase imediatamente: le-la de volta (swap-in), expulsar outra pagina para abrir espaco, precisar daquela novamente, e assim por diante. Esse ciclo e thrashing, e a CPU gasta seu tempo esperando transferencias de paginas em vez de fazer trabalho real.

$ uptime
 14:32:10 up 8 days,  3:11,  2 users,  load average: 18.40, 16.92, 12.05

O load average dispara, mas o top mostra baixo uso de CPU (us/sy) e um alto wa (I/O wait). "A CPU esta ociosa mas tudo esta lento" e a assinatura classica de thrashing.

Premissas (ambiente alvo)

  • SO: Ubuntu / Linux em geral
  • Sintoma: o servidor de repente fica lento, nao responde, ate o SSH trava
  • Swap esta habilitado (a linha Swap no free nao e 0)
  • Voce pode ler vmstat / free / /proc (configuracoes permanentes requerem sudo)

Por que observar a taxa e nao o uso do swap?

Conclusao: Swap em uso nao e um problema por si so. Paginas de processos ociosos descansando no swap e saudavel. O perigo e quando o fluxo de swap-in / swap-out e continuo e rapido -- somente isso se correlaciona com a lentidao perceptivel. Julgue pela taxa (vmstat si/so), nao pelo uso (free).

Mesmo se free -h mostrar o Swap cheio, isso pode ser apenas "expulso ha muito tempo e deixado la", o que e inofensivo. Por outro lado, uso moderado de swap com trafego pesado por segundo fara o sistema parecer congelado.

Aspecto Saudavel Thrashing
Uso do swap (free) Estavel, mesmo se alto Oscila rapido para cima e baixo
si/so (vmstat) Proximo de 0 Continuamente grande
CPU Normal Alto wa (I/O wait)
Sensacao Normal Cada acao esta lenta

O indicador principal de thrashing nao e "quanto swap esta preenchido" mas "quanto esta sendo movido para dentro e fora por segundo agora". A primeira coisa a verificar sao as colunas si / so do vmstat.

Como observar primeiro? (vmstat / free)

Conclusao: Execute vmstat 1. Se si (swap-in KB/s) e so (swap-out KB/s) permanecerem grandes, o thrashing esta confirmado. Cruze com a margem disponivel com free -h e o alto wa com top.

Comece transmitindo o vmstat em intervalos de 1 segundo. A primeira linha e uma media desde o boot, mas cada linha subsequente cobre o ultimo segundo, entao voce ve o "fluxo" de si/so.

$ vmstat 1
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 2 14 2087600  51200  10240 102400 4096 5120  4200 5300 3100 8900  3  4  8 85  0
 1 12 2091200  48300  10100 101800 3800 4900  3900 5100 2980 8500  2  3 10 85  0

si / so continuam fluindo em megabytes por segundo e wa (I/O wait) esta acima de 80%. b (processos bloqueados) tambem esta alto. Isso e evidencia concreta de thrashing. Em seguida, verifique a margem disponivel.

$ free -h
               total        used        free      shared  buff/cache   available
Mem:            3.8Gi       3.5Gi       120Mi        12Mi       180Mi        90Mi
Swap:           2.0Gi       1.9Gi        80Mi

available e minusculo e o Swap esta quase cheio. A memoria fisica esta esgotada e nao e mais possivel descarregar para o swap. Confirme com top.

$ top -bn1 | head -5
top - 14:32:40 up 8 days,  load average: 18.40, 16.92, 12.05
Tasks: 210 total,   1 running, 209 sleeping
%Cpu(s):  3.0 us,  4.0 sy,  0.0 ni,  8.0 id, 85.0 wa,  0.0 hi,  0.0 si
MiB Mem :   3891.0 total,    120.0 free,   3591.0 used,    180.0 buff/cache
MiB Swap:   2048.0 total,     80.0 free,   1968.0 used

wa domina e id (idle) e minusculo. A CPU esta esperando a conclusao de I/O, nao computando.

Se o sar (pacote sysstat) estiver disponivel, voce pode revisar o historico com sar -W (pswpin/s, pswpout/s para swap-in/out) e sar -B (pgpgin/s, pgpgout/s para paginacao) para ver "quando comecou". Combinar o vmstat em tempo real com o historico do sar facilita identificar o momento de inicio.

Qual processo esta consumindo o swap?

Conclusao: smem -s swap -r e a forma mais legivel de ver o uso de swap por processo. Se o smem nao estiver disponivel, some VmSwap de /proc/<pid>/status. O maior consumidor de swap geralmente e a causa principal do thrashing.

O smem pode mostrar o uso de swap por processo diretamente (sudo apt install smem).

$ sudo smem -s swap -r | head
  PID User     Command                         Swap      USS      PSS      RSS
 2314 www-data java -Xmx3g -jar app.jar      1245184   210432   215300   240128
 1190 mysql    /usr/sbin/mysqld               412300    98200   101400   130560
 1532 root     /usr/bin/dockerd               120400    40100    42300    61440

O processo com a coluna Swap desproporcional e o culpado. Se voce nao pode instalar o smem (adicionar um novo pacote e indesejavel), some diretamente do /proc.

# Listar VmSwap de todos os processos, maior primeiro (KB)
$ for f in /proc/[0-9]*/status; do
    awk '/^Name:/{n=$2} /^VmSwap:/{print $2, n, FILENAME}' "$f"
  done 2>/dev/null | sort -rn | head
1245184 java /proc/2314/status
412300 mysqld /proc/1190/status
120400 dockerd /proc/1532/status

Uma reserva de memoria mal configurada (um -Xmx da JVM maior que a RAM fisica, um buffer pool de banco de dados superdimensionado, etc.) e a causa tipica. Reduzir o limite para um valor que cabe na memoria fisica geralmente e a correcao real.

Alto uso de swap nao significa automaticamente "o culpado". Para encontrar "o processo movendo paginas para dentro e fora agora", capture o smem varias vezes durante o thrashing e observe quais processos tem um valor de Swap variavel. Um valor estatico sao apenas paginas dormindo.

Como parar a lentidao agora? (Primeiros socorros)

Conclusao: Parar um processo descontrolado ou superdimensionado, ou reduzir seu limite de memoria e reinicia-lo, e a correcao mais rapida. Para reiniciar o swap acumulado, use swapoff -a && swapon -a, mas executa-lo sem memoria livre suficiente convida o OOM killer, entao e perigoso. Os primeiros socorros devem apenas ir na direcao de "liberar memoria".

O passo mais seguro e eficaz e parar ou reconfigurar o processo superdimensionado encontrado com smem.

# Reiniciar o culpado pelo caminho adequado (apos revisar sua configuracao)
$ sudo systemctl restart myapp.service

Para "reiniciar" trazendo as paginas em swap de volta para a RAM, use swapoff -> swapon. Mas isso recarrega todo o conteudo do swap na memoria de uma vez, entao se a memoria fisica livre for menor que o uso do swap, o OOM killer e acionado.

# Perigoso: convida OOM se a memoria livre for menor que o uso do swap
$ free -h          # Confirme que available > Swap used primeiro
$ sudo swapoff -a && sudo swapon -a

"Apenas reiniciar" faz o sintoma desaparecer, mas ele voltara a menos que voce corrija a configuracao de memoria ou o vazamento. Apos os primeiros socorros, sempre prossiga para identificar a causa raiz (configuracao superdimensionada / vazamento / RAM simplesmente insuficiente).

Deve-se ajustar o swappiness?

Conclusao: vm.swappiness e um valor de tendencia (0-200, padrao 60; valores acima de 100 sao para swap rapido como zram/zswap) para "quao agressivamente usar swap". Reduzi-lo torna o kernel menos ansioso para expulsar paginas de aplicacao, o que pode melhorar a sensacao de um servidor interativo. Mas nao resolve a escassez de memoria fisica em si. A correcao real e mais RAM ou menos uso.

Verifique o valor atual.

$ cat /proc/sys/vm/swappiness
60

Reduza temporariamente e observe (reverte no reboot).

# Comece em torno de 10 em vez de 0
$ sudo sysctl -w vm.swappiness=10

Uma vez confirmado o efeito, torne permanente.

$ echo 'vm.swappiness = 10' | sudo tee /etc/sysctl.d/99-swappiness.conf
$ sudo sysctl --system
swappiness Tendencia Indicado para
0-10 Evitar swap o maximo possivel Servidores interativos, baixa latencia
60 (padrao) Equilibrado Uso geral
100 Swap agressivamente Batch, orientado a throughput
Acima de 100 (ate 200) Swap ainda mais agressivamente Swap rapido como zram/zswap

Definir swappiness como 0 nao desabilita completamente o swap (ainda faz swap sob pressao de memoria). Reduzi-lo demais tambem desequilibra o cache de arquivos e pode criar um tipo diferente de lentidao. Evite valores extremos e ajuste observando vmstat para si/so se estabilizarem.

Como limitar o swap de um servico com cgroups?

Conclusao: Para um servico gerenciado pelo systemd, MemoryMax / MemoryHigh impoem um limite de memoria e contem o thrashing para que um servico nao prejudique todo o host. Quando o limite e excedido, apenas aquele servico e submetido a recuperacao / OOM, e o restante fica protegido.

Defina um limite de memoria em um servico especifico (ex: uma aplicacao Java superdimensionada).

$ sudo systemctl edit myapp.service

Escreva os limites na secao [Service].

[Service]
MemoryHigh=2G
MemoryMax=2.5G

MemoryHigh e um "limite suave" que aciona recuperacao agressiva quando excedido; MemoryMax e um "limite rigido" que aciona OOM kill quando excedido. Aplique apos salvar.

$ sudo systemctl daemon-reload
$ sudo systemctl restart myapp.service
$ systemctl show -p MemoryMax myapp.service

Agora o uso de memoria do servico e limitado pelo cgroup e nao espalha mais thrashing por todo o sistema.

Definir MemoryHigh um pouco abaixo de MemoryMax cria uma zona de amortecimento onde o servico "e limitado e fica lento" antes de ser subitamente morto no limite rigido. Em producao, especificar ambos e mais seguro do que apenas MemoryMax.

E se a causa real for RAM insuficiente? (Adicionando Swap / Capacidade)

Conclusao: Se nao ha configuracao errada e nenhum vazamento, e a memoria e simplesmente sempre escassa, adicionar hardware (mais RAM) e o caminho correto. Adicionar swap e apenas um buffer "melhor do que morrer por OOM"; mesmo com mais swap, thrashing continua lento. Adicionar swap ganha tempo; adicionar RAM resolve.

Passos para adicionar um arquivo de swap para evitar OOM iminente (quando voce nao pode adicionar RAM imediatamente, ex: na nuvem).

# Criar um arquivo de swap de 2GB
$ sudo fallocate -l 2G /swapfile
$ sudo chmod 600 /swapfile
$ sudo mkswap /swapfile
$ sudo swapon /swapfile

Torne permanente adicionando ao /etc/fstab.

/swapfile  none  swap  sw  0  0

Adicionar swap nao para a paginacao enquanto a memoria esta escassa, entao continua "lento". Adicionar swap e um paliativo para evitar OOM kills; nao cura a lentidao do thrashing em si. Se voce sofre thrashing cronico, mais RAM ou reduzir a carga de trabalho (reduzir limites de memoria por processo, reduzir concorrencia) e a abordagem correta.

Checklist quando ainda nao melhora

Conclusao: Thrashing significa que "memoria fisica insuficiente" esta confirmada. Confirme si/so com vmstat -> identifique o culpado com smem -> corrija a configuracao superdimensionada / vazamento -> adicione RAM se ainda insuficiente. A causa converge nessa camada. swappiness e cgroups sao sintomaticos; a raiz e o balanco com a quantidade de memoria.

  • [ ] Confirmou que si / so no vmstat 1 estao continuamente grandes (taxa, nao uso)?
  • [ ] Confirmou que wa (I/O wait) esta alto enquanto us/sy estao baixos no top?
  • [ ] Verificou a margem available e do Swap com free -h?
  • [ ] Identificou o culpado com smem -s swap -r ou VmSwap em /proc/*/status?
  • [ ] A configuracao de memoria daquele processo (JVM -Xmx, buffers de banco) cabe na RAM fisica?
  • [ ] Descartou vazamento de memoria (nao esta aumentando monotonicamente ao longo do tempo)?
  • [ ] Confirmou available > Swap used antes dos primeiros socorros (swapoff)?
  • [ ] Considerou mais RAM / menos concorrencia se cronico?

Proximas leituras