---
id: a5d668f7af98fc92ee33094885d591a3
author: snowmerak
title: 'Et øjeblik med grøn te, som Go-runtime''en ellers ville tage fra os: GreenTea GC'
description: Go's nye GreenTea GC forbedrer ydeevnen ved at øge effektiviteten af affaldsindsamling for små objekter baseret på hukommelsesspænd (memory spans). Vi gennemgår de eksisterende problemer med GC og fordelene ved GreenTea GC.
language: da
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
---

## Hvorfor kommer der endnu en ny GC?

### Hvorfor den eksisterende GC

Den nuværende GC i Go kan beskrives med følgende begreber.

- concurrent tri-color mark and sweep
  - Den bruger i alt tre farver (hvid, grå, sort) til at spore tilstanden for objekter.
    - Hvid: objekter, som endnu ikke er besøgt
    - Grå: objekter, der er besøgt, men hvis børneobjekter ikke alle er besøgt
    - Sort: objekter, hvis børneobjekter alle er besøgt
    - Når gennemgangen er afsluttet, indsamles alle hvide objekter.
  - Den bruger en Write Barrier til at beskytte hukommelse, der oprettes under GC'en.
    - Tidspunktet for at aktivere/deaktivere Write Barrier udgør en betydelig del af det, der almindeligvis kaldes Stop-The-World (herefter STW).
  - Denne proces udføres samtidigt.
- No Compaction
  - Go's GC udfører ikke compaction.
  - Hukommelsesfragmentering kan forekomme.
    - Dog minimerer objekter på 32 KB eller derunder fragmenteringen ved at udnytte Go's hukommelsesallokators per-P-cache.
- Non-Generational
  - Go's GC håndterer ikke objekter opdelt efter generationer.
  - Alle objekter hører til den samme generation.
- Escape Analysis
  - Go bestemmer gennem Escape Analysis, om et objekt allokeres på heapen eller stakken.
  - Groft sagt kan man antage, at hvis der anvendes dinglende pointere eller interfaces, allokeres objektet på heapen.

### Det vigtige

Go's GC gennemsøger alle objekter fra roden og udfører tri-color mark undervejs. Denne proces kan kortfattet beskrives som `topologisk sorteret grafs samtidige gennemsøgningsalgoritme`. Men hvert objekt befinder sig sandsynligvis i forskellige hukommelsesområder. Kort sagt:

1. Antag, at der er to hukommelsesområder, der ligger ca. 32 MB fra hinanden.
2. I hvert af disse hukommelsesområder er der allokeret fem objekter.
   1. Objekt A, B og C er i område 1.
   2. Objekt D og E er i område 2.
3. Referencerækkefølgen for objekterne er `A -> D -> B -> E -> C`.
4. Når GC starter, besøges A, derefter D, derefter B, derefter E og til sidst C.
5. Da A og D befinder sig i forskellige hukommelsesområder, skal der skiftes hukommelsesområde, når A besøges og derefter D.
6. I denne proces skiftes hukommelsesområde, og nye cache-linjer fyldes op; dette medfører såkaldte hukommelsesspringomkostninger. En betydelig del af applikationens CPU-tid kan blive brugt på dette GC-arbejde.
7. Denne omkostning opstår kontinuerligt, mens GC'en kører.

### Så hvad er problemet?

Denne adfærd i GC'en er et stort problem i følgende tilfælde.

- Når antallet af kerner er højt, og hukommelsen er stor
  - Go's GC udføres som udgangspunkt samtidigt.
  - Men når hukommelsesområdet er stort, og objekterne er spredt, vil flere kerner samtidigt gennemsøge objekter i forskellige hukommelsesområder.
  - Hvis bussen mellem CPU og hukommelse ikke er tilstrækkelig stor, kan hukommelsesbåndbredden blive en flaskehals.
  - Da der også er en fysisk afstand, vil hvert enkelt opslag desuden medføre en relativt stor forsinkelse.
- Når der er mange små objekter
  - Hvis man driver træer eller grafer med stor dybde eller mange børn i Go, skal GC gennemsøge alle disse objekter.
  - Hvis objekterne er spredt over forskellige hukommelsesområder, opstår forsinkelsen på grund af det første forhold.
  - Når der er mange små objekter, skal GC gennemsøge dem alle, og derfor vil CPU-kerner under GC'en bruge en betydelig del af deres kapacitet på GC-arbejde.

For at løse disse problemer har Golang-teamet præsenteret GreenTea GC.

## Du har ikke længere tid til at drikke mere grøn te

### Hvad tager den grønne te fra os

Det område, hvor den hurtigste løsning på den eksisterende GC kan anvendes, synes at være hukommelseslokalitet. Det vil sige, at man placerer objekter tæt på hinanden for at minimere hukommelsesspring. Men man kan ikke tvinge programmørens kodningsmønstre, og objektallokering kan ikke forudsiges, da den afhænger af arbejdsgangen.

Golang-teamets valgte metode er derfor måske **hukommelsesspænd (Memory Span)**.

### Hvad er et hukommelsesspænd?

Et hukommelsesspænd er et forholdsvis stort hukommelsesområde, der er allokeret til at rumme små objekter. Den nye GC, der er navngivet GreenTea GC, udfører affaldsindsamling på disse hukommelsesspænd. Detaljerne i driften er næsten identiske med den eksisterende Tri-Color Mark and Sweep.

### GreenTea GC's funktionsmåde

Først allokerer GreenTea GC et hukommelsesspænd. Størrelsen er, som tidligere beskrevet, 8 KiB, hvilket er en vis størrelse. Inden for dette kan der allokeres objekter på op til 512 bytes. Det er nogenlunde den størrelse, man vanskeligt kan overstige for noderne i de træer eller grafer, vi tidligere nævnte, eller for strukturer, der typisk flyttes til heapen. Objekter på 512 bytes eller derunder ophobes i dette hukommelsesspænd, hver gang de flyttes til heapen, og når spænd er fyldt, allokeres et nyt hukommelsesspænd.

Når GC'en udløses, lægger GreenTea GC disse hukommelsesspænd i en kø og gennemgår dem sekventielt. I denne proces anvender GreenTea GC en skedulering, der næsten svarer til den eksisterende GMP-model. Også områder som work stealing er implementeret. Under alle omstændigheder henter den worker, der tager et hukommelsesspænd fra køen, og gennemgår de objekter, der ligger i det tildelte hukommelsesspænd. I denne proces anvendes Tri-Color Mark and Sweep på samme måde.

### Hvilken fordel er der?

Set i det store er der kun én væsentlig forskel fra den eksisterende GC i denne proces. Den enhed, GC'en udfører arbejde på ad gangen, er ændret fra **objekt** til **hukommelsesspænd**. Dette giver GC'en følgende fordele.

- Hukommelseslokalitet: Fordi objekter samles i et hukommelsesspænd, minimeres hukommelsesspring, når objekter gennemsøges. Det betyder, at CPU-cachen kan udnyttes maksimalt.
- Forbedret GC-ydelse: Ved at udføre GC pr. hukommelsesspænd fungerer GC'en mere effektivt i multikerneomgivelser.
- Optimeret hukommelsesallokering: Fordi hukommelsesspænd allokeres med fast størrelse, reduceres allokeringsomkostningerne ved små objekter, og risikoen for fragmentering falder. Dette øger effektiviteten af frigivelse af hukommelse.

Kort sagt kan vi fra nu af allokere objekter på 512 bytes eller derunder oftere og med en lettere sindstilstand.

Der er dog også visse gunstige situationer.

- Træ-/grafstrukturer: Når fan-out er høj, og der sjældent sker ændringer
  - B+-træer, Trie, AST (Abstract Syntax Tree)
  - Cache-venlige datastrukturer
- Batchbehandling: Arbejdsgange med allokering af mange små data
  - Mange små objekter, der genereres ved JSON-parsing
  - DAO-objekter fra databasens resultatsæt

I disse tilfælde kan GreenTea GC yde bedre end den eksisterende GC. Den gælder dog ikke i alle situationer, og især når objekter er spredt over forskellige hukommelsesområder, er det stadig vanskeligt at forvente en dramatisk ydelsesforbedring.

## Så

Golang-teamet har sandsynligvis til hensigt at fortsætte med at forbedre GC'en på lang sigt. Denne GreenTea GC kan ses som en mindre ændring i et lille område og erstatter ikke den eksisterende GC. Men GreenTea GC er et interessant eksempel, der giver indblik i de problemer, som Golang-teamet står over for eller forudser. Selvom det er et overraskende komplekst problem, var forsøget på at løse det ved at tilføje et relativt simpelt koncept imponerende. Personligt synes jeg, det er et interessant eksempel.

GreenTea GC er et eksperimentelt funktion, der introduceres fra Go 1.25. Den kan aktiveres ved at bruge miljøvariablen `GOEXPERIMENT=greenteagc`. Da funktionen stadig er eksperimentel, kræves der grundig test, før den tages i brug i produktionsmiljøer.

## Referencer

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