Middle
What do you know about garbage collectors in Go?
sobes.tech AI
Answer from AI
Golang uses a concurrent and low-latency garbage collector based on the mark-sweep algorithm.
Main features:
- Three-color algorithm: Marks objects with three colors: white (potential garbage), gray (reachable but its children are not inspected), and black (reachable objects and their children).
- Parallelism: Works in parallel with user goroutines on multiple cores, minimizing stop-the-world times.
- Concurrence: Most of the GC work is performed concurrently with the program execution.
- Low-latency Stop-The-World: The stop-the-world (STW) pause time is extremely short, used only for the "start of marking" and "end of marking" phases to ensure object graph consistency.
- Re-usable scans: Stack scanning is also performed in parallel and concurrently with STW.
- Heap growth target: GC aims to maintain a certain percentage of heap fill after each cycle to avoid too frequent or too rare cycles. This threshold can be controlled via the
GOGCvariable. By default,GOGC=100, meaning GC runs when the heap size doubles.
The GC cycle consists of the following phases:
- GCstart: A short STW phase. Root objects for marking are identified.
- GCmark: A concurrent phase. The GC traverses the object graph, marking reachable objects (gray -> black). Uses write barriers to track graph changes during marking.
- GCmarktermination: A short STW phase. Completes marking and switches from write barrier to sweep.
- GCsweep: A concurrent phase. Unreachable (white) objects are freed. This phase can occur in the background or as needed when allocating new memory.
GC in Go is managed automatically and does not require manual invocation in most cases. GC performance can be monitored using the environment variable GODEBUG=gctrace=1.
Example of monitoring:
$ GODEBUG=gctrace=1 go run your_program.go
The gctrace output shows information about each GC cycle, including phase times, heap size, and STW time.
To optimize GC, besides setting GOGC, consider:
- Reducing allocations (e.g., reusing buffers, avoiding unnecessary conversions).
- Profiling for "escape analysis" to understand which objects are allocated on the heap.
- Using pools (
sync.Pool) for frequently created and released objects.