Garbage collection (GC) in Go is een automatisch geheugenbeheersysteem dat voor het eerst verscheen in de vroege versies van de taal. Historisch gezien was GC in Go een van de objecten van kritiek vanwege de invloed op de prestaties. Echter, met de ontwikkeling van de taal, vooral na versie Go 1.5, is het aanzienlijk verbeterd: nu wordt er een drievoudige concurrente (concurrent, tricolor, mark-and-sweep) garbage collector gebruikt met minimale pauzes (low-pause GC).
Probleem ontstaat wanneer programma's een groot aantal tijdelijke objecten aanmaken, of wanneer ze geen verwijzingen naar niet-gebruikte structuren verwijderen: dit verhoogt de belasting van de GC en kan leiden tot lange pauzes. Bijzondere aandacht moet worden besteed aan de typen objecten, cyclische verwijzingen en lange schakels van verwijzingen die buiten de stack liggen.
Oplossing - houd de geheugentoewijzing in de gaten, gebruik profiling en tuning van GC via de omgevingsvariabele GOGC, minimaliseer het aantal allocaties in interne lussen en kritieke secties. Het is belangrijk om te onthouden dat garbage collection in Go alleen werkt voor de heap: alles wat op de stack is toegewezen, wordt automatisch verwijderd wanneer het uit het zicht verdwijnt, en objecten die "naar de heap gaan" worden door de GC beheerd.
Voorbeeldcode:
// Profilering van allocatie en optimalisatie GC import ( "runtime" "fmt" ) func main() { var memStats runtime.MemStats runtime.ReadMemStats(&memStats) fmt.Printf("Voor allocatie: %d bytes ", memStats.Alloc) s := make([]int, 1_000_000) for i := range s { s[i] = i } runtime.GC() // handmatige opruiming runtime.ReadMemStats(&memStats) fmt.Printf("Na GC: %d bytes ", memStats.Alloc) }
Belangrijke kenmerken:
GOGC (bijvoorbeeld, GOGC=100 - standaard; verlaging versnelt de GC, maar verhoogt het CPU-gebruik).Hoe weet je welke objecten "naar de heap gaan" en welke op de stack blijven?
Antwoord: Dit wordt bepaald door escape analysis, die kan worden geanalyseerd met de compiler-vlag go build -gcflags="-m". Objecten die buiten de functie worden geretourneerd of in closures worden gebruikt, gaan meestal naar de heap.
Voorbeeldcode:
func escape() *int { v := 42 return &v // v zal naar de heap gaan }
Heeft GC invloed op alle variabelen, inclusief die op de stack?
Nee, GC werkt alleen met de heap (objects allocated on the heap). Alles wat op de stack is toegewezen, wordt automatisch opgeruimd bij het beëindigen van de functie.
Kan een handmatige aanroep van runtime.GC() de prestaties aanzienlijk verbeteren?
Integendeel, een handmatige aanroep verergert vaak de prestaties, waardoor het CPU-gebruik toeneemt. Gebruik dit alleen voor tests of foutopsporing.
Negatieve case
Een ontwikkelaar schrijft een service die in elke aanvraag nieuwe grote slices aanmaakt, zonder na te denken over hun levensduur. Dit leidt snel tot een verhoogde belasting van de GC, scherpe pauzes en prestatieproblemen.
Voordelen:
Nadelen:
Positieve case
Een ontwikkelaar optimaliseert de service, profileert allocaties, hergebruikt buffers via sync.Pool, verlaagt de frequentie van GC-aanroepen en minimaliseert geheugentoewijzing op kritieke plekken.
Voordelen:
Nadelen: