---
id: a5d668f7af98fc92ee33094885d591a3
author: snowmerak
title: En kopp grønn te å miste til Go-kjøretiden, GreenTea GC
description: Go sin nye GreenTea GC forbedrer ytelsen ved å øke effektiviteten av søppelinnsamling for små objekter, basert på minnespenn (memory spans). Vi ser nærmere på de eksisterende problemene med GC og fordelene ved GreenTea GC.
language: "no"
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 det en ny GC igjen

### Hvorfor den eksisterende GC-en

Den nåværende GC-en i Go kan beskrives med følgende begreper.

- concurrent tri-color mark and sweep
  - Den bruker totalt tre farger (hvit, grå, svart) for å spore tilstanden til objekter.
    - Hvit: objekter som ennå ikke er besøkt
    - Grå: objekter som er besøkt, men hvis barnobjekter ikke alle er besøkt
    - Svart: objekter der alle barnobjekter er besøkt
    - Når gjennomgangen er ferdig, samles alle hvite objekter inn.
  - Den bruker en Write Barrier for å beskytte minne som opprettes under GC-en.
    - Tiden for å slå Write Barrier av og på utgjør en betydelig del av det som vanligvis kalles Stop-The-World (heretter STW).
  - Denne prosessen utføres samtidig.
- No Compaction
  - Go-GC-en utfører ikke compaction.
  - Minnefragmentering kan oppstå.
    - Objekter på 32 KB eller mindre minimerer likevel fragmenteringen ved å utnytte den per-P-cachen som Go-minneallokatoren (Allocator) bruker.
- Non-Generational
  - Go-GC-en håndterer ikke objekter etter generasjon.
  - Alle objekter tilhører samme generasjon.
- Escape Analysis
  - Go bruker Escape Analysis til å avgjøre om et objekt allokeres på heapen eller på stacken.
  - Grovt sett kan man si at hengende pekere eller bruk av grensesnitt (interface) fører til allokering på heapen.

### Det viktige

Go-GC-en utfører tri-color mark ved å traversere alle objekter fra roten. Hvis prosessen skal oppsummeres i én linje, kan man kalle den `samtidig traversering av en topologisk sortert graf`. Imidlertid er det sannsynlig at hvert objekt ligger i forskjellige minneområder. Kort sagt:

1. Anta at det finnes to minneområder som ligger omtrent 32 MB fra hverandre.
2. I hvert av disse minneområdene er fem objekter allokert.
   1. Objekt A, B og C ligger i område 1.
   2. Objekt D og E ligger i område 2.
3. Referanserekkefølgen for objektene er `A -> D -> B -> E -> C`.
4. Når GC-en starter, besøkes A, deretter D, B, E og til slutt C.
5. Her ligger A og D i forskjellige minneområder, så man må flytte seg mellom minneområder når man går fra A til D.
6. I denne prosessen flyttes man mellom minneområder og nye cache-linjer må fylles, altså det som kalles minnehoppkostnad (memory jump cost). En betydelig del av CPU-tiden som applikasjonen bruker, kan gå med til dette GC-arbeidet.
7. Denne kostnaden oppstår kontinuerlig så lenge GC-en pågår.

### Hva er problemet?

Denne oppførselen til GC-en er spesielt problematisk i følgende tilfeller.

- Når antall kjerner er stort og minnet er stort
  - Go-GC-en utføres som standard samtidig.
  - Men når minneområdet er stort og objektene er spredt, vil flere kjerner samtidig søke etter objekter i ulike minneområder.
  - Hvis bussen mellom CPU og minne ikke er tilstrekkelig stor, kan minnebåndbredden bli en flaskehals.
  - Dessuten gir den fysiske avstanden at hvert søk tar relativt lang tid.
- Når det finnes mange små objekter
  - Hvis dere driver med trær eller grafer med stor dybde eller mange barn i Go, må GC-en traversere alle disse objektene.
  - Hvis objektene er spredt over ulike minneområder, oppstår forsinkelser på grunn av den første årsaken.
  - Når det finnes mange små objekter, må GC-en dessuten traversere dem alle, så CPU-kjerner bruker mye kapasitet på GC-en mens den kjører.

For å løse disse problemene har Golang-teamet presentert GreenTea GC.

## Du har ikke lenger tid til mer grønn te

### Hva tar den grønne teen?

Det ser ut til at den raskeste løsningen på den eksisterende GC-en er å ta for seg minnelokalitet. Det vil si å plassere objekter nær hverandre for å minimere minnehopp. Men man kan ikke tvinge programmererens kodemønstre, og objektallokering er uforutsigbar avhengig av arbeidsflyten.

Derfor er det Golang-teamet valgte tilnærmingen **minnespenn (Memory Span)**.

### Hva er et minnespenn?

Et minnespenn er et område der det allokeres en relativt stor minneblokk for å plassere små objekter. Den nye GC-en kalt GreenTea GC utfører søppelinnsamling på disse minnespennene. Detaljene i virkemåten er nesten identiske med den eksisterende Tri-Color Mark and Sweep.

### Virkemåten til GreenTea GC

Først allokerer GreenTea GC et minnespenn. Størrelsen er, som beskrevet tidligere, 8 KiB, altså en viss størrelse. Inni dette kan det allokeres objekter på opptil 512 bytes. Størrelsen på noder i trær eller grafer, som nevnt i eksemplene, eller på strukturer som typisk flyttes til heapen, er vanskelig å se for seg som større enn dette. Objekter på 512 bytes eller mindre hopes opp i dette minnespennet hver gang de flyttes til heapen, og når minnespennet er fullt, allokeres et nytt minnespenn.

Når GC-en kjører, legger GreenTea GC disse minnespennene i en kø og kontrollerer dem sekvensielt. I denne prosessen bruker GreenTea GC en planlegging som nesten er lik den eksisterende GMP-modellen. Også funksjonalitet som work stealing er implementert. Uansett tar arbeideren som henter et minnespenn fra køen, og sjekker objektene inni det tildelte minnespennet. I denne prosessen brukes Tri-Color Mark and Sweep på samme måte.

### Hva er fordelen?

Når man ser på helheten, er det bare én hovedforskjell fra den eksisterende GC-en. Enheten GC-en utfører arbeid på i én operasjon har endret seg fra **objekt** til **minnespenn**. Dette gir GC-en følgende fordeler.

- Minnelokalitet: Fordi objektene samles i et minnespenn, minimeres minnehopp når objekter traverseres. Det vil si at CPU-cachen kan utnyttes best mulig.
- Forbedret GC-ytelse: Ved å utføre GC per minnespenn fungerer GC-en mer effektivt i flerkjerne-miljøer.
- Optimalisert minneallokering: Fordi minnespenn allokeres med fast størrelse, reduseres overheaden ved allokering av små objekter, og sannsynligheten for fragmentering minker. Dette øker effektiviteten ved frigjøring av minne.

Kort sagt kan vi fra nå av allokere objekter på 512 bytes eller mindre oftere og med lettere sinn.

Likevel finnes det noen områder der dette er gunstig.

- Trær- og grafstrukturer: Tilfeller med høy fan-out og sjelden endring
  - B+-trær, Trie, AST (Abstract Syntax Tree)
  - Cache-vennlige datastrukturer
- Batch-prosessering: Arbeidsflyter med massiv allokering av små data
  - Mange små objekter som oppstår ved JSON-parsing
  - DAO-objekter fra databasens resultatsett

I disse tilfellene kan GreenTea GC yte bedre enn den eksisterende GC-en. Den gjelder imidlertid ikke i alle situasjoner, og spesielt når objektene er spredt over ulike minneområder, er det fortsatt vanskelig å forvente dramatiske ytelsesforbedringer.

## Så

Golang-teamet har sannsynligvis til hensikt å fortsette å forbedre GC-en på lang sikt. Denne GreenTea GC-en kan ses på som en mindre endring i et begrenset område, og den erstatter ikke den eksisterende GC-en. Likevel fremstår GreenTea GC som et interessant eksempel som gir innsikt i problemene Golang-teamet står overfor, eller forventer å møte. Det er et overraskende komplekst problem, men tilnærmingen om å løse det ved å legge til et relativt enkelt konsept var imponerende. Personlig synes jeg dette er et interessant eksempel.

GreenTea GC er en eksperimentell funksjon som introduseres fra Go 1.25. Den kan aktiveres ved å sette miljøvariabelen `GOEXPERIMENT=greenteagc`. Siden funksjonen fortsatt er eksperimentell, kreves det grundig testing før den tas i bruk i produksjonsmiljøer.

## Referanser

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