>_ josetilabs.com
← blog

09 out 2026 · infraestrutura, sistemas operacionais, go · 5 min

Swap não é RAM grátis: pausas do GC do Go sob pressão de memória

O caso original

Em setembro, Fernando Simões publicou uma medição incômoda. Um serviço Go rodando num cgroup, com swap habilitado para absorver picos de memória. A pausa mediana do coletor de lixo era de 51 µs. A pior chegou a 40 ms. Desses 40 ms, 39 ms foram gastos em 228 page faults dentro de uma pausa stop-the-world. Em 30 minutos, 312 pausas do mesmo tipo.

A causa é estrutural. O GC do Go guarda metadados fora do heap, numa região que o runtime nunca devolve ao sistema. Essas páginas são lidas a cada ciclo de coleta. O kernel escolhe o que mandar para o swap pela idade, e as páginas de metadados, pouco acessadas em relação ao resto, acabam saindo. Quando o coletor vai lê-las, o processo inteiro está parado esperando o dispositivo responder.

A pergunta

O autor mediu com swap em NVMe. Eu queria medir com swap em zram, que é o que roda nesta VM: páginas comprimidas na própria RAM. Se o page fault é resolvido descomprimindo em memória, o custo por página deveria cair bastante, e a pausa de 40 ms não deveria aparecer. A latência de alocação, essa sim, continua sendo paga.

Como eu testei

Resultados

métrica teto de 2 GB, swap de 512 MB teto de 380 MB, swap de 3 GB
pausa stop-the-world p99 196 µs 196 µs
pausa stop-the-world máxima 196 µs 196 µs
faults maiores do processo 0 69.026
latência de alocação p50 1,2 ms 1,4 ms
latência de alocação p99 3,4 ms 9,7 ms
latência de alocação máxima 11,0 ms 22,2 ms
pswpin no intervalo 11.198 páginas 71.676 páginas
pswpout no intervalo 44.813 páginas 126.199 páginas

Os valores de pausa são o limite superior do bucket do histograma do Go, não medições contínuas: nenhuma pausa passou de 196 µs. Os contadores de swap são globais da máquina, não do processo.

O que mudou e o que não mudou

A pausa de stop-the-world não apareceu. Nem perto: 196 µs nas duas condições, contra 40 ms no caso do NVMe. O que apareceu foi latência localizada na goroutine que aloca: o p99 subiu de 3,4 ms para 9,7 ms e o pior caso de 11 ms para 22 ms, com 69 mil faults maiores contra zero.

Esse custo é pago só por quem está alocando, não pelo processo inteiro como uma pausa de GC. Mas é o custo que o usuário sente como resposta lenta. Vale lembrar o outro número do artigo original: montar uma mensagem de 511 KiB, que levava de 3 a 5 ms, passou a levar 105 ms com swap em NVMe e 903 ms com volume de rede.

Ressalvas de método

As duas rodadas foram únicas, e esta VM não está ociosa. Antes do teste ela já tinha 3,4 GB de dados no zram, comprimidos em 779 MB (razão de 4,6 para 1), e 17,5 milhões de faults maiores desde o boot, três dias antes. O swap global se movimenta sem mim, então os deltas de pswpin e pswpout entram na tabela como ordem de grandeza, não como medida isolada do meu processo.

O que é limpo na comparação: as duas rodadas usaram o mesmo alocador, o mesmo Go, o mesmo hardware e o mesmo tamanho de carga. Só mudou o teto de memória do cgroup.

Por que a diferença vem do dispositivo

Um page fault maior custa o tempo de buscar a página e trazê-la de volta. Com swap em NVMe, isso significa leitura de disco: fila, submissão de bio, espera. Com zram, significa descompressão em memória. A ordem de grandeza muda, e a pausa segue a ordem de grandeza. A causa está no dispositivo atrás do swap, não no coletor do Go.

O autor também mediu o Green Tea GC, o coletor que entrou no Go 1.26, e não viu impacto relevante na leitura dos metadados. Eu rodei com Go 1.27.1.

O que fazer

  1. Meça o seu caso. Pausa de GC depende de tamanho de heap, taxa de alocação e dispositivo de swap. A conclusão de outra pessoa não se transfere.
  2. Isole o serviço em cgroup. Use memory.high antes de memory.max: você recebe aviso antes de o kernel começar a evictar com agressividade.
  3. Escolha o swap com intenção. zram resolve o caso do disco lento, mas não todos: quando enche, não tem para onde mandar as páginas frias e o kernel perde margem. zswap comprime igual, mas na frente de um swap em disco, com despejo automático das páginas mais antigas.
  4. Deixe margem. Esta VM tem 5,8 GB e vive com swap em uso. Se o seu serviço precisa de p99 baixo, RAM sobrando é mais barata que ajuste fino de swap.

Leia também

// Por e-mail

Os textos publicados aqui também vão por e-mail. O envio ainda não começou: quem deixar o endereço agora entra na primeira lista.

Guardo só o seu e-mail, a data e o registro desta autorização. Sem cookies, sem rastreador e nada compartilhado com terceiros. Detalhes na política de privacidade.