---
id: a5d668f7af98fc92ee33094885d591a3
author: snowmerak
title: 'A xícara de chá verde de tempo livre que o runtime do Go tomará de você: GreenTea GC'
description: O novo GreenTea GC do Go melhora o desempenho ao aumentar a eficiência da coleta de objetos pequenos com base em spans de memória. Examinamos os problemas do GC existente e as vantagens do GreenTea GC.
language: pt
date: 2025-07-16T11:02:04.831022221Z
path: /blog/posts/greentea-gc:-the-respite-of-a-cup-of-green-tea-seized-by-the-go-compiler-z72acc3f7
category: lowlevel
---

## Por que surge outro GC novo

### Por que o GC existente

O GC atual do Go pode ser descrito pelos seguintes termos.

- concurrent tri-color mark and sweep
  - Utiliza um total de 3 cores (branco, cinza e preto) para acompanhar o estado dos objetos.
    - Branco: objeto ainda não visitado
    - Cinza: objeto visitado, mas cujos objetos filhos ainda não foram todos visitados
    - Preto: objeto cujos objetos filhos foram todos visitados
    - Ao término da travessia, todos os objetos brancos são coletados.
  - Utiliza uma Write Barrier para proteger a memória criada durante o GC.
    - O tempo de ativação e desativação da Write Barrier corresponde a uma parte significativa do que comumente se chama Stop-The-World (doravante STW).
  - Este processo é executado de forma concorrente.
- No Compaction
  - O GC do Go não realiza compactação.
  - Pode ocorrer fragmentação de memória.
    - No entanto, objetos de até 32 KB utilizam o cache per-P do alocador de memória (Allocator) da linguagem Go, minimizando a fragmentação.
- Non-Generational
  - O GC do Go não gerencia os objetos por gerações.
  - Todos os objetos pertencem à mesma geração.
- Escape Analysis
  - O Go determina, por meio da Escape Analysis, se um objeto é alocado no heap ou na stack.
  - De forma aproximada, pode-se considerar que, se houver ponteiros pendentes (dangling pointers) ou o uso de interfaces, o objeto é alocado no heap.

### O que é importante

O GC do Go percorre todos os objetos a partir da raiz e executa a marcação tricolor. Esse processo pode ser resumido em uma linha como `algoritmo de travessia concorrente de grafo com ordenação topológica`. Contudo, cada objeto provavelmente está em regiões de memória distintas. Em termos simples:

1. Suponha que existam duas regiões de memória separadas por cerca de 32 MB.
2. Cada uma dessas regiões de memória contém 5 objetos alocados.
   1. Os objetos A, B e C estão na região 1.
   2. Os objetos D e E estão na região 2.
3. A ordem de referências entre os objetos é `A -> D -> B -> E -> C`.
4. Quando o GC começa, ele visita A, depois D, depois B, depois E e, por fim, C.
5. Nesse caso, como A e D estão em regiões de memória diferentes, é necessário trocar de região de memória ao visitar D após A.
6. Nesse processo, ocorre a chamada penalidade de salto de memória (memory jump), que envolve a troca de região de memória e o preenchimento de novas linhas de cache. Uma parcela significativa do tempo de CPU utilizado pela aplicação pode ser destinada a essas operações de GC.
7. Esse custo continua a ocorrer durante todo o período em que o GC estiver em execução.

### Então, qual é o problema?

O comportamento desse GC é fatal nos seguintes casos.

- Quando há muitos núcleos e a memória é grande
  - O GC do Go é executado, por padrão, de forma concorrente.
  - Entretanto, quando a região de memória é ampla e os objetos estão dispersos, vários núcleos passam a percorrer objetos em diversos espaços de memória simultaneamente.
  - Nesse processo, se o barramento entre a CPU e a memória não for suficientemente largo, a largura de banda de memória pode se tornar um gargalo.
  - Além disso, por haver distância física real, cada travessia pode introduzir um atraso relativamente grande.
- Quando há muitos objetos pequenos
  - Se você opera árvores ou grafos com profundidade grande ou muitos filhos em Go, o GC precisa percorrer todos esses objetos.
  - Nesse processo, se os objetos estiverem dispersos em diferentes regiões de memória, ocorre atraso pelo primeiro motivo mencionado.
  - Além disso, havendo muitos objetos pequenos, o GC precisa percorrer todos eles, de modo que, durante a execução do GC, os núcleos da CPU dedicam uma quantidade considerável de recursos ao GC e ao trabalho que ele realiza.

Para resolver esses problemas, a equipe do Golang apresentou o GreenTea GC.

## Você não tem mais tempo para tomar chá verde

### O que tira o chá verde

Parece que a área na qual a equipe considerou possível aplicar a solução mais rápida para o GC existente foi a localidade de memória. Ou seja, trata-se de posicionar os objetos próximos uns dos outros para minimizar os saltos de memória. Contudo, não é possível impor padrões aos programadores que escrevem o código e, além disso, a alocação de objetos é imprevisível de acordo com o fluxo de trabalho.

Talvez por isso, a abordagem escolhida pela equipe do Golang tenha sido o **span de memória (Memory Span)**.

### O que é um span de memória?

Um span de memória é uma área que recebe uma alocação de memória relativamente grande e nela aloca objetos pequenos. E o novo GC, denominado GreenTea GC, executa a coleta de lixo sobre esses spans de memória. Os detalhes de operação são quase idênticos ao tradicional Tri-Color Mark and Sweep.

### Funcionamento do GreenTea GC

Primeiramente, o GreenTea GC aloca um span de memória. Seu tamanho, conforme descrito anteriormente, é de 8 KiB, um tamanho considerável. Nele, podem ser alocados objetos de até 512 bytes. Esse é aproximadamente o tamanho máximo que os nós de árvores ou grafos mencionados como exemplo, ou as estruturas comuns que escapam para o heap, costumam ter. Objetos de até 512 bytes são acumulados nesse span de memória sempre que escapam para o heap, e, quando o span fica cheio, um novo span de memória é alocado.

Quando ocorre um GC, o GreenTea GC enfileira esses spans de memória e os inspeciona sequencialmente. Nesse processo, o GreenTea GC utiliza um escalonamento quase semelhante ao modelo GMP existente. Também estão implementados mecanismos como o roubo de trabalho (work stealing). De qualquer forma, o worker que retira um span de memória da fila inspeciona os objetos internos do span que lhe foi atribuído. Nesse processo, o Tri-Color Mark and Sweep é utilizado da mesma forma.

### Qual é a vantagem?

Considerando o processo como um todo, a diferença em relação ao GC existente resume-se, em grandes linhas, a um único ponto: a unidade de execução do GC passou de **objeto** para **span de memória**. Por causa disso, o GC apresenta as seguintes vantagens.

- Localidade de memória: como os objetos estão agrupados no span de memória, os saltos de memória são minimizados ao percorrer os objetos. Ou seja, é possível aproveitar ao máximo o cache da CPU.
- Melhoria de desempenho do GC: ao executar o GC na unidade de span de memória, o GC passa a operar de forma mais eficiente em ambientes multicore.
- Otimização da alocação de memória: como os spans de memória são alocados com tamanho fixo, o overhead de alocação ao criar objetos pequenos diminui, e a possibilidade de fragmentação se reduz. Isso aumenta a eficiência da liberação de memória alocada.

Em resumo, a partir de agora podemos alocar objetos de até 512 bytes com mais frequência e com mais tranquilidade.

Contudo, também existem certos cenários nos quais essa abordagem é vantajosa.

- Estruturas de árvore/grafo: casos com alto fan-out e poucas alterações
  - B+ trees, Tries, AST (Abstract Syntax Tree)
  - Estruturas de dados amigáveis ao cache
- Processamento em lote: fluxos de trabalho com alocação em grande volume de dados pequenos
  - Múltiplos objetos pequenos gerados pelo parsing de JSON
  - Objetos DAO de conjuntos de resultados de banco de dados

Nesses casos, o GreenTea GC pode apresentar desempenho superior ao GC existente. No entanto, isso não se aplica a todas as situações e, especialmente quando os objetos estão dispersos em diferentes regiões de memória, ainda é difícil esperar melhorias de desempenho dramáticas.

## Então

Parece que a equipe do Golang pretende continuar aprimorando o GC a longo prazo. Este GreenTea GC pode ser visto como uma mudança pequena em uma área restrita e não substitui o GC existente. Contudo, o GreenTea GC é um caso interessante que permite vislumbrar os problemas que a equipe do Golang enfrenta ou prevê. Embora seja um problema complexo, foi impressionante a tentativa de resolvê-lo pela adição de um conceito relativamente simples. Pessoalmente, considero-o um caso interessante.

O GreenTea GC é um recurso experimental introduzido a partir do Go 1.25. Ele pode ser ativado utilizando a variável de ambiente `GOEXPERIMENT=greenteagc`. Como este recurso ainda é experimental, são necessários testes suficientes antes de utilizá-lo em ambientes de produção.

## Referências

- [github.com/golang/go - runtime: green tea garbage collector #73581](https://github.com/golang/go/issues/73581)
