IFCE Horizonte · Portal
ADS23 · Sistemas Operacionais

Mão na massa · ~48 min

Lendo a memória de verdade — e provocando um OOM

Hoje você aprende a ler os números certos da memória (o campo que importa não é o que a maioria olha), localiza os maiores consumidores do sistema, e fecha provocando de propósito a morte de um container por falta de memória.

Passos 1–4 · Diagnóstico de memóriaPassos 5–7 · Reforço Docker: limite e OOM

Passo 1 de 7

A visão geral da memória

O primeiro comando que qualquer pessoa roda pra saber como está a memória de uma máquina.
1free -h

O que é

Mostra memória total, usada, livre, em cache e disponível. O -h converte pra unidades legíveis (Gi, Mi) em vez de bytes.

Por que importa

É a primeira resposta pra "a máquina está sem memória?" — desde que você olhe a coluna certa.

$ free -h
            total    used    free   shared  buff/cache   available
Mem:          3.8Gi   1.2Gi   0.4Gi    12Mi       2.2Gi       2.3Gi
Swap:         2.0Gi      0B   2.0Gi
O que esperar:

repare que free (livre) parece baixo, mas available (disponível) está alto. Isso é normal e desejável — o Linux usa memória ociosa como cache de disco e devolve assim que alguém precisar.

Então qual coluna eu devo olhar?

available. É quanto realmente dá pra alocar agora sem recorrer a swap. A coluna used sozinha engana, e free quase sempre parece assustadoramente baixa num sistema saudável.

na sua VM, qual a diferença entre os valores de "free" e "available"? Por que essa diferença existe?

0 / 40 caracteres

Passo 2 de 7

A versão crua, direto do kernel

Como sempre desde o Encontro 2 — o comando bonito é só um leitor organizado de um arquivo em /proc.
2head -8 /proc/meminfo

O que é

O arquivo que o free lê por baixo dos panos. Tem dezenas de campos; as primeiras linhas já trazem o essencial.

Por que importa

Confirma que não há mágica: MemAvailable aqui é exatamente a coluna available do passo anterior.

$ head -8 /proc/meminfo
MemTotal:        3986432 kB
MemFree:          438292 kB
MemAvailable:    2405118 kB
Buffers:           83104 kB
Cached:          1912440 kB
O que esperar:

os valores em kB. Divida por 1024 duas vezes pra chegar em GB e conferir que bate com o free -h do passo 1.

Passo 3 de 7

Quem está consumindo mais

Saber que a memória está apertada é metade do diagnóstico. A outra metade é descobrir quem é o responsável.
3ps aux --sort=-%mem | head -6

O que é

--sort=-%mem ordena a lista por percentual de memória, do maior pro menor. O head -6 pega o cabeçalho + os 5 maiores.

Por que importa

É exatamente assim que se identifica o candidato a vazamento numa máquina de produção.

$ ps aux --sort=-%mem | head -6
USER    PID %CPU %MEM     VSZ    RSS TTY  STAT COMMAND
aluno  2417  0.3  4.2 2841096 167284 ?    Sl   /usr/lib/firefox/firefox
aluno  1804  0.8  3.9 4105216 158920 ?    Ssl  /usr/bin/gnome-shell
root    891  0.0  1.1 1534208  44120 ?    Ssl  /usr/lib/snapd/snapd
aluno  1930  0.0  0.3   11520   5236 pts/0 Ss  bash
O que esperar:

a coluna RSS (Resident Set Size), em kB, é a memória física ocupada por aquele processo; VSZ é o espaço de endereçamento reservado. RSS é o número a comparar entre processos. Anote o PID do maior, você vai usá-lo no próximo passo.

Passo 4 de 7

A memória de um processo específico

Use o PID que você anotou no passo 3 para ver os detalhes de memória daquele processo.
4grep Vm /proc/<PID>/status

O que é

Filtra os campos de memória virtual daquele processo. Troque <PID> pelo número que você anotou.

Por que importa

VmRSS é a memória física de verdade; VmSize é o espaço de endereçamento reservado — quase sempre bem maior, e isso é normal.

$ grep Vm /proc/2417/status
VmSize:  2841096 kB
VmRSS:    167284 kB
VmData:   712408 kB
VmStk:       132 kB
O que esperar:

VmSize muito maior que VmRSS. Isso é o espaço de endereçamento do slide 3 em ação — o processo "reservou" muito mais do que realmente está usando na RAM.

Deu "Arquivo ou diretório inexistente"

O processo pode ter terminado entre o passo 3 e o 4. Rode o ps aux --sort=-%mem | head -6 de novo e use um PID atualizado.

Reforço Docker · limite de memória e OOM

Passo 5 de 7

Um container com limite apertado

Agora vamos criar um container com um teto baixo de memória de propósito, e fazê-lo tentar ultrapassar esse teto.
5docker run -d --name memtest --memory=64m --memory-swap=64m alpine sh -c "cat /dev/zero | head -c 500m | tail"

O que é

--memory=64m limita o container a 64 MB de RAM, e --memory-swap=64m (o mesmo valor) proíbe o uso de swap além disso. O comando interno tenta acumular 500 MB na memória: o tail guarda tudo, porque espera uma quebra de linha que /dev/zero nunca manda.

Por que importa

Reproduz, de forma controlada e segura, exatamente o cenário que derruba serviços em produção.

$ docker run -d --name memtest --memory=64m --memory-swap=64m alpine sh -c "cat /dev/zero | head -c 500m | tail"
b7e2a90c4f13d8a5...

Importante

⚠️ Importante: mantenha os dois limites. São eles que garantem que o estrago fique contido dentro do container — sem eles, esse mesmo comando pode travar sua VM inteira.

Por que também o --memory-swap?

Com só --memory=64m, o Docker deixa o container usar mais 64 MB de swap. O container não morre ao bater nos 64 MB: primeiro fica lento, jogando memória para o disco. Igualar os dois valores faz o teto ser de 64 MB mesmo, e o OOM acontecer na hora.

Passo 6 de 7

Confirmando a morte por OOM

Aguarde alguns segundos e verifique o que aconteceu com o container.
6docker ps -a --filter name=memtest

O que é

-a mostra também containers já encerrados — necessário aqui, porque o nosso deve ter morrido.

Por que importa

O código de saída 137 (128 + 9) quer dizer "morto com SIGKILL", o sinal que o OOM Killer usa. Junto com OOMKilled: true, é a assinatura de morte por falta de memória — reconhecê-la é competência direta de sustentação.

$ docker ps -a --filter name=memtest
NAME      IMAGE    STATUS
memtest   alpine   Exited (137) 4 seconds ago
O que esperar:

Exited (137). Confirme com docker inspect memtest --format '{{.State.OOMKilled}}' — deve retornar true. O 137 sozinho não basta: um docker kill também dá 137, mas com OOMKilled falso.

Meu container terminou com código 0, sem OOM

Quase sempre é o swap: confira se você digitou o --memory-swap=64m no passo 5. Remova o container (docker rm memtest) e repita o passo 5 com os dois limites.

o que aconteceu com o container memtest e por quê? O que o código de saída 137 indica?

0 / 40 caracteres

Passo 7 de 7

Limpando

Remova o container de teste antes de encerrar.
7docker rm memtest

O que é

Remove o registro do container já encerrado (não precisa de docker stop — ele já morreu sozinho).

Por que importa

Containers encerrados continuam ocupando espaço em disco até serem removidos.

O que esperar:

o nome memtest impresso de volta, confirmando a remoção.

livre: algo que te surpreendeu ou uma dúvida que sobrou de hoje

0 / 40 caracteres

Exemplo de observação forte × fraca

Observação fraca

"O container morreu porque ficou sem memória."

Observação forte

"O container saiu com código 137 e OOMKilled: true. Ele tentou acumular 500 MB, mas o --memory=64m, sem swap extra, impôs um teto de 64 MB; ao ultrapassá-lo, o kernel matou o processo com SIGKILL, sem chance de salvar estado. O importante é que o estrago ficou contido: a VM continuou funcionando normalmente, porque o limite isolou o problema naquele container."

Checklist final

  • Rodei free -h e identifiquei a diferença entre free e available
  • Conferi que o /proc/meminfo bate com o free
  • Listei os 5 maiores consumidores de memória e anotei um PID
  • Comparei VmSize com VmRSS de um processo
  • Subi um container com --memory=64m e provoquei o OOM
  • Confirmei o Exited (137) / OOMKilled: true
  • Removi o container de teste
  • Preenchi as 3 observações acima (vou colar no quiz semanal)
0 / 8 verificados