jasoncc.github.io

Random Signals on Some Low-level Stuff.

View on GitHub

The Go Gargabe Collector (GC)

key points:

The generational hypothesis

It has been known since 1984 [7] that most allocations “die young” i.e. become garbage very soon after being allocated. This observation is called the generational hypothesis and is one of the strongest empirical observations in the entire PL engineering space. It has been consistently true across very different kinds of programming languages and across decades of change in the software industry.

Discovering this fact about programs was useful because it meant GC algorithms could be designed to take advantage of it. These new generational collectors has lots of improvements over the old stop-mark-sweep style:

They also introduce some downsides:

Still, the benefits are so huge that basically all modern GC algorithms are generational.

The Go Concurrent Collector

As Go is a relatively ordinary imperative language with value types, its memory access patterns are probably comparable to C# where the generational hypothesis holds and thus .NET uses a generational collector.

Go programs exhibit strongly generational behavior, and the Go team are exploring potentially expoliting that in future with something they call the “request oriented collector”[8]. It has been observed that this is essentially a renamed generational GC with a tweaked tenuring policy.

Despite that, Go’s current GC is not generational. It just runs a plain old mark/sweep in the background.

Go optimises for pause times as the expense of throughput to such an extent that it seems willing to slow down your program by almost any amount in order to get event just slightly faster pauses. (But it’s still too expensive to a program working as an OS kernel)

Conclusion from Mike Hearn

But if you take one thing away, let it be this: garbage collection is a hard problem, really hard, one that has been studied by an army of computer scientists for decades. So be very suspicious of supposed breakthroughs that everyone else missed. They are more likely to just be strange or unusual tradeoffs in disguise, avoided by others for reasons that may only become apparent later.

GC Knobs

Profiling and Tracing

There are many ways to get started with profiling, see go doc runtime/pprof. After getting a .prof file, use pprof -http=:8080 cpu.prof to play with it. Note that it’s not go tool’s pprof, get it, go get github.com/google/pprof.

$ GODEBUG=gctrace=1 ./myserver
import (
  "runtime/trace"
  "os"
)
...
f, err := os.Create("trace.out")
defer f.Close()
err = trace.Start(f)
defer trace.Stop()
go tool trace <binary> trace.out

References

  1. Pusher: GC in theory and practice
  2. The Go Garbage Collector (GC)
  3. Go GC: Prioritizing low latency and simplicity
  4. (Java)GC Algorithms: Implementations
  5. Why golang garbage-collector not implement Generational and Compact gc?
  6. Modern garbage collection–A look at the Go GC strategy
  7. Generation Scavenging: A Non-disruptlve High Perfornm Storage Reclamation Algorithm
  8. (ROC)Request Oriented Collector
  9. Go GC:Latency Problem Solved
  10. Andrey Sibiryov, Uber, Golang’s Garbage
  11. Go’s march to low-latency GC
  12. Getting to Go

back