How does sync.Pool work, how does it interact with the GC, and what are the common misuse pitfalls?
Question 228HardGo 1.22 to 1.25
sync.Pool is a cache of temporary objects, sharded per P (processor), that reduces allocation pressure. Get tries, in order:
- the local P's private slot,
- the local P's shared queue,
- stealing from other Ps,
- the victim cache,
- calling
New.
GC interaction: since Go 1.13, each GC moves the primary cache into a victim cache and throws away the old victim. An object survives at most about two GC cycles. A pool is not a connection pool or a free list, and you must never rely on an object still being there.
var bufPool = sync.Pool{
New: func() any { return new(bytes.Buffer) },
}
func render(w io.Writer, v any) error {
buf := bufPool.Get().(*bytes.Buffer)
buf.Reset() // always reset: state from the previous user remains
defer func() {
if buf.Cap() <= 64<<10 { // don't keep huge buffers alive
bufPool.Put(buf)
}
}()
if err := json.NewEncoder(buf).Encode(v); err != nil {
return err
}
_, err := w.Write(buf.Bytes())
return err
}
Pitfalls:
- Putting a slice (
[]byte) by value allocates when it is converted toany(staticcheck SA6002). Store pointers instead. - Forgetting to reset leaks data between requests. This can be a security bug.
- Pooling objects of very different sizes pins memory (Go issue 23199).
- Using an object after
Putis a use-after-free style bug.
Rule: only add a pool once a profile shows allocation is the bottleneck.
More on Concurrency Patterns & sync
- Q226Explain sync.Once semantics, including panics, and the OnceFunc / OnceValue / OnceValues helpers.
- Q227When would you use sync.Cond, and why must Wait always be called in a loop?
- Q229When should you use sync.Map instead of a map guarded by a Mutex?
- Q230What are typed atomics (atomic.Int64, atomic.Pointer[T]) and why prefer them over atomic.AddInt64 / atomic.Value?
- Q231Implement a lock-free "store max" using compare-and-swap. What is the ABA problem, and does it affect Go?
- Q232Explain the Go memory model and "happens-before". What synchronization edges does Go guarantee?