Pular para o conteúdo principal
Blog

Memória, zRAM e swap no Linux com agentes no terminal

Atualizado em 7 de outubro de 2026

Linux
Agentes
Performance

Congelamentos no Linux com agentes no terminal: a zRAM de 32 GB que piorou os travamentos, /tmp na RAM com o pnpm e swap em dois níveis no Btrfs.

O meu notebook começou a travar com frequência depois que passei a rodar vários agentes de IA no terminal. As ventoinhas ficavam no máximo, a máquina esquentava e o sistema congelava no meio de chamadas do Google Meet. Ao mesmo tempo, o /tmp vivia com quase 6 GB ocupados sem motivo aparente.

A máquina é um notebook com processador Intel Core Ultra 5 125H, 16 GB de RAM e Arch Linux em Btrfs. Quem enfrenta falta de memória costuma aumentar o espaço de swap. Fiz isso e subi a zRAM para 32 GB. O resultado foi o oposto do esperado, pois os travamentos pioraram.

A investigação dos travamentos

O primeiro passo foi consultar os logs do systemd-oomd, o serviço que encerra processos antes que o kernel congele por falta de memória. Encontrei o registro do travamento:

systemd-oomd: Marked app-... for killing due to memory used (14.8G / 16.1G)
and swap used (30.9G / 34.3G) being more than 90.00%

A memória física estava em 14,8 GB e a swap em 30,9 GB. O computador tinha atingido o limite simultâneo de RAM e swap.

A minha swap estava toda em zRAM. A zRAM não fica no disco. Ela cria um bloco de memória virtual compactado dentro da própria RAM física.

No meu sistema, a zRAM usava o algoritmo zstd, com compressão média de 3 para 1. Quando os agentes ocuparam 30,9 GB de swap virtual, o kernel precisou de cerca de 10 GB de RAM física só para guardar as páginas compactadas.

O computador tem 15,5 GB de RAM útil, então sobraram 5 GB para o ambiente gráfico, os navegadores, as chamadas de vídeo e os próprios agentes. As 18 threads do processador passaram a disputar ciclos compactando e descompactando memória com zstd. A CPU atingiu o limite térmico, derrubou o clock e a interface congelou.

O acúmulo invisível no /tmp

O segundo problema estava no /tmp. No Arch Linux, o /tmp é montado em tmpfs, que reside na RAM e pode consumir até metade da memória física (7,6 GB).

Ao medir as pastas com o du, encontrei o que ocupava 5,8 GB da partição:

  • /tmp/.pnpm-store com 1,3 GB
  • 332 pastas com nomes aleatórios do Vitest somando 1,1 GB
  • worktrees do Git criadas por agentes, com 1,8 GB
  • /tmp/node-compile-cache com 467 MB

O pnpm guarda os pacotes em uma store global e usa hard links para não duplicar arquivos entre projetos. Hard links não atravessam sistemas de arquivos. Quando um agente cria um clone ou worktree dentro do /tmp, o pnpm detecta que a pasta está no tmpfs e não no /home. Sem poder criar os links, ele baixa uma cópia completa de todas as dependências para dentro de /tmp/.pnpm-store, que passa a ocupar 1,3 GB da RAM.

Cada suíte de testes do Vitest em modo SSR também criava uma pasta temporária com identificador único, e os arquivos ficavam para trás quando a execução era interrompida.

As correções

Dividi a solução em quatro partes: limpeza do /tmp, worktrees fora da RAM, swap em dois níveis e limites de processo e concorrência.

Worktrees fora da RAM e limpeza automática

O primeiro ajuste foi proibir worktrees em /tmp. O caminho padrão passou a ser ~/.cache/worktrees/, no disco principal. Nesse diretório, o pnpm consegue usar os hard links da store global sem ocupar RAM.

Para as pastas abandonadas não se acumularem, criei ~/.config/user-tmpfiles.d/worktrees.conf:

e %h/.cache/worktrees - - - 3d

Ativei o timer do usuário no systemd:

systemctl --user enable --now systemd-tmpfiles-clean.timer

O sistema agora apaga qualquer pasta em ~/.cache/worktrees sem acesso por mais de 3 dias.

Swap em dois níveis com prioridades

Para os congelamentos pararem sem as chamadas do Meet caírem por falta de memória, montei um swap em dois níveis:

flowchart LR
  accTitle: Swap em dois níveis
  accDescr: O kernel grava primeiro na zram0, 16 GB comprimidos em RAM com prioridade 100. O arquivo de 32 GB no SSD, com prioridade 0, só recebe dados quando a zRAM enche.
  K[Kernel] -->|grava primeiro| Z["zram0<br/>16 GB em RAM<br/>lzo-rle · pri=100"]
  K -->|reserva| S["/swap/swapfile<br/>32 GB no SSD<br/>Btrfs · pri=0"]

No Linux, o número de prioridade define onde o kernel grava primeiro. Como 100 é maior que 0, o sistema direciona as gravações para a zRAM. O arquivo no SSD fica com 0 bytes e só recebe dados quando a zRAM enche. É a margem de segurança.

Troquei o algoritmo da zRAM de zstd para lzo-rle. O lzo-rle comprime um pouco menos, mas gasta muito menos CPU.

No Btrfs com snapshots do Snapper, o arquivo de swap não pode ficar no subvolume raiz, pois snapshots não suportam arquivo de swap ativo. Criei um subvolume dedicado em /swap:

sudo btrfs subvolume create /swap
sudo btrfs filesystem mkswapfile --size 32g /swap/swapfile
sudo swapon --priority 0 /swap/swapfile

No /etc/fstab, a entrada ficou:

/swap/swapfile none swap defaults,pri=0 0 0

Limites de memória e concorrência no Node e no Vitest

O teto de heap de cada processo Node.js vem do --max-old-space-size, e o default fica na casa dos 4 GB. Quando os agentes rodam vários scripts de lint e build juntos, esse consumo soma rápido.

Defini o teto de 2 GB em ~/.config/environment.d/50-dev-limits.conf e no ~/.bashrc:

export NODE_OPTIONS="--max-old-space-size=2048"
export VITEST_MAX_THREADS=4

A variável VITEST_MAX_THREADS=4 impede que o Vitest lance 18 workers simultâneos durante os testes e mantém a CPU dentro do limite térmico do notebook.

Regras de concorrência para os agentes

No arquivo de instruções que todos os meus agentes leem, o ~/.agents/CONTEXT.md, adicionei uma regra explícita:

- **Limite de concorrência em máquina local**: Nunca execute mais de 2 subagents simultaneamente quando a tarefa envolver compilação (tsc/build), execução de testes (vitest/jest/playwright) ou navegadores headless.

Resultados

Depois das mudanças, o /tmp caiu de 5,8 GB para 120 MB em uso estável.

Com a zRAM em lzo-rle e o arquivo de 32 GB no SSD como reserva na prioridade 0, o notebook parou de travar e de derrubar chamadas no Google Meet. A temperatura do processador ficou dentro da faixa normal mesmo com várias suítes de teste rodando juntas.

Referências