한국어English日本語简体中文繁體中文DeutschไทยTiếng ViệtРусскийPortuguês (Brasil)EspañolBahasa Indonesia

Guia do Lag em Jogos › L10 Memória

Pausa stop-the-world do GC no servidor Stop-the-world GC pause

ID da causa mem-gc · Responsável principal Desenvolvimento do servidor (Equipe de desenvolvimento) · Também envolvidos Infraestrutura de servidores (Equipe de infraestrutura)

Abrir o card interativo, com figuras e simulações →

Para coletar o lixo, um servidor em Java ou C# interrompe todas as threads (stop-the-world), e o servidor inteiro fica parado enquanto isso dura.

Por quê O heap enche e o GC começa → Efeito Todas as threads do jogo param durante a coleta (quanto mais dados vivos, mais demora) → Na tela Travamento para todos no servidor ao mesmo tempo, seguido de avanço rápido

Sintomas
Travamento, Avanço rápido
Fatores
Paralisação
Quem é afetado
Servidor inteiro
Quando
Em intervalos regulares, Quanto mais tempo ligado
Responsável
Responsável principal Desenvolvimento do servidor (Equipe de desenvolvimento) · Também envolvidos Infraestrutura de servidores (Equipe de infraestrutura)
O que fazer (Equipe de desenvolvimento)
Definir explicitamente nas opções de inicialização um GC de pausas curtas (ZGC, Shenandoah ou G1 com meta de pausa menor), reduzir alocações, ajustar o tamanho do heap.
O que fazer (Equipe de infraestrutura)
Usar instâncias com memória suficiente para um heap folgado, dar aos contêineres pelo menos 2 CPUs e cerca de 1,8 GB de memória (abaixo disso, o JDK 26 e anteriores escolhem o Serial GC como padrão), monitorar o tempo de pausa do GC.
Números de referência
O Minor GC, que coleta só os objetos novos (geração young), leva de alguns a dezenas de ms. O Full GC, que coleta o heap inteiro com vários GB de dados vivos, pode passar de 1 s. O ZGC fica abaixo de 1 ms quase independentemente do tamanho do heap, e o Shenandoah também tem pausas curtas, que não crescem com o tamanho do heap.
No gráfico
Picos em intervalos regulares · Tempo de tick do servidor, tempo de pausa do GC
Onde olhar
Ativar o log do GC e sobrepor o horário e a duração de cada pausa ao gráfico de tempo de tick do servidor. No Java, as linhas Pause da opção de inicialização -Xlog:gc* (-XX:+PrintGCDetails no JDK 8 e anteriores); no .NET, a métrica de pausa do GC no dotnet-counters (dotnet.gc.pause.time a partir do .NET 9, % Time in GC since last GC no 8 e anteriores); no Go, a linha que o GODEBUG=gctrace=1 registra a cada GC
Confirma se
Os picos de tick coincidem com as pausas do GC, e a duração da pausa é parecida com a do pico. Todas as zonas e canais do servidor dão pico no mesmo instante
Descarta se
Picos de tick sem pausas longas no log do GC: outra causa, como lock, chamada síncrona ou escrita em disco. Só uma zona dá pico: GC da engine de script (mem-script-gc) ou carga daquela zona
Como verificar
Ferramentas de infra (sem precisar do código do jogo)
Saiba mais
No Java, a meta padrão de cada pausa do G1 é 200 ms, o que equivale a 4 ticks em um servidor de 20 ticks. Se o contêiner recebe menos de 2 CPUs ou menos de cerca de 1,8 GB de memória, o Java do JDK 26 e anteriores escolhe como padrão o Serial GC, que coleta com uma única thread, e as pausas ficam bem mais longas. Servidores em C# (.NET) costumam ativar o GC de servidor e o GC em segundo plano, mas as coletas das gerações 0 e 1 (Gen0/1), que guardam os objetos novos, e o Full GC com compactação continuam parando todas as threads. No Go, as pausas costumam ficar abaixo de 1 ms, mas, com muita alocação, quem pede memória precisa assumir parte do trabalho do GC, e o tick fica mais lento. Em qualquer modelo, se a alocação for mais rápida que a coleta, a thread do jogo acaba parando: o G1 passa para um Full GC, e o ZGC segura a thread que pediu memória até a coleta terminar.
Casos reais
Riot Games 2021: Queda de 5 horas no EUW do League of Legends: um BD auxiliar parou o servidor inteiro

Fontes

  1. Garbage-First (G1) Garbage Collector Oracle
    Meta de pausa padrão do G1 de 200 ms (MaxGCPauseMillis); se a memória acaba durante a coleta, passa para um Full GC, que para e compacta o heap inteiro
  2. JEP 523: Make G1 the Default Garbage Collector in All Environments OpenJDK
    Até agora, com 1 CPU ou menos de 1.792 MB de memória, o Serial GC era escolhido como padrão; a partir do JDK 27, o G1 é o padrão em qualquer ambiente
  3. JEP 439: Generational ZGC OpenJDK
    Pausas do ZGC de no máximo 1 ms, independentes do tamanho do heap; pausas do G1 de alguns ms a alguns segundos. Se a alocação for mais rápida que a recuperação, há risco de parada de alocação (allocation stall)
  4. JEP 189: Shenandoah: A Low-Pause-Time Garbage Collector (Experimental) OpenJDK
    O tempo de pausa do Shenandoah é parecido, seja o heap de 200 MB ou de 200 GB
  5. Background garbage collection .NET
    O GC em segundo plano só se aplica às coletas da geração 2; as coletas das gerações 0 e 1 (GC em primeiro plano) param todas as threads gerenciadas
  6. A Guide to the Go Garbage Collector Go
    O GC do Go roda quase todo de forma concorrente e só tem pausas stop-the-world curtas; com muita alocação, as goroutines assumem parte do trabalho do GC (assist), o que gera atraso
  7. JEP 271: Unified GC Logging OpenJDK
    A partir do JDK 9, o log do GC foi reimplementado com o logging unificado (-Xlog); -Xlog:gc gera uma linha por GC, como o antigo -XX:+PrintGC
  8. The java Command Oracle
    Tabela de conversão das opções antigas de log do GC para -Xlog: -XX:+PrintGCDetails vira -Xlog:gc*
  9. dotnet-counters diagnostic tool .NET
    A partir do .NET 9, aparece como medidores do System.Runtime (dotnet.gc.pause.time e outros); no .NET 8 e anteriores, como os antigos EventCounters (% Time in GC since last GC e outros)
  10. runtime package Go
    GODEBUG=gctrace=1: uma linha por GC, com o tempo de relógio (wall clock) de cada fase, o tamanho do heap no início e no fim do GC e o heap alvo

Veja também

Mesma camada: L10 Memória

Mesmo sintoma (Travamento) em outras camadas

Ver o card interativo, com figuras e simulações