Garbage collection (GC) in Go is an automatic memory management mechanism that first appeared in the early versions of the language. Historically, GC in Go has been one of the sources of criticism due to its impact on performance. However, with the development of the language, especially after version Go 1.5, it has been significantly improved: now it uses a three-color concurrent mark-and-sweep garbage collector with low pauses.
The problem arises when programs create a large number of temporary objects or when they do not remove references to unused structures: this increases the load on GC and can lead to long pauses. Pay special attention to object types, cyclic references, and long chains of references that lie outside the stack.
The solution is to monitor memory allocation, use profiling and tuning of GC through the environment variable GOGC, minimizing the number of allocations in inner loops and critical sections. It is important to remember that garbage collection in Go works only for the heap: everything allocated on the stack will be automatically freed when it goes out of scope, and objects that have "gone" to the heap are controlled by GC.
Code example:
// Profiling alloc and optimizing GC import ( "runtime" "fmt" ) func main() { var memStats runtime.MemStats runtime.ReadMemStats(&memStats) fmt.Printf("Before allocation: %d bytes ", memStats.Alloc) s := make([]int, 1_000_000) for i := range s { s[i] = i } runtime.GC() // manual cleanup runtime.ReadMemStats(&memStats) fmt.Printf("After GC: %d bytes ", memStats.Alloc) }
Key features:
GOGC (e.g., GOGC=100 is the default; lowering it speeds up GC but increases CPU usage).How to know which object "goes" to the heap and which remains on the stack?
Answer: This is determined using escape analysis, which can be analyzed using the compiler flag go build -gcflags="-m". Objects that are returned out of a function or used in closures often go to the heap.
Code example:
func escape() *int { v := 42 return &v // v will be on the heap }
Does GC affect all variables, including those on the stack?
No, GC only works with heap-allocated objects. Everything allocated on the stack is cleared automatically upon function completion.
Can a manual call to runtime.GC() significantly improve performance?
On the contrary, manual calls often degrade performance by increasing CPU consumption. It should only be used for testing or debugging cases.
Negative case
A developer writes a service that creates new large slices in each request without considering their lifespan. This quickly leads to increased load on the GC, significant pauses, and performance degradation.
Pros:
Cons:
Positive case
A developer optimizes the service, profiles allocations, reuses buffers via sync.Pool, reduces GC call frequency, and minimizes memory allocation in hot spots.
Pros:
Cons: