Lab 04: sync.Pool & Allocators

Time: 45 minutes | Level: Advanced | Docker: docker run -it --rm golang:1.22-alpine sh

Overview

Master sync.Pool for amortizing allocation costs, implement the bytes.Buffer pooling pattern, benchmark with -benchmem, and understand arena allocator concepts.


Step 1: sync.Pool Basics

package main

import (
	"bytes"
	"fmt"
	"sync"
)

var bufPool = sync.Pool{
	New: func() interface{} {
		// Called when pool is empty
		buf := new(bytes.Buffer)
		buf.Grow(256) // pre-allocate
		return buf
	},
}

func processRequest(data string) string {
	buf := bufPool.Get().(*bytes.Buffer)
	buf.Reset() // always reset before use
	defer bufPool.Put(buf)

	buf.WriteString("processed: ")
	buf.WriteString(data)
	return buf.String()
}

func main() {
	// First call: pool is empty, New() is called
	result1 := processRequest("hello")
	fmt.Println(result1)

	// Second call: pool reuses the buffer
	result2 := processRequest("world")
	fmt.Println(result2)
}

💡 Key insight: sync.Pool objects may be collected by GC at any time. Never store state that must survive a GC cycle. The pool is a hint, not a guarantee.


Step 2: Object Pool for HTTP Responses


Step 3: Benchmark — Pool vs No-Pool

Run:

📸 Verified Output:


Step 4: bytes.Buffer Pooling Pattern


Step 5: GC Interaction with sync.Pool

💡 sync.Pool is cleared on each GC cycle. This is intentional — it prevents memory leaks. The pool is a performance optimization, not a long-lived cache. For long-lived pools, use a channel-based approach.


Step 6: Channel-Based Permanent Pool


Step 7: Arena Allocator Concept (Go 1.20+)

Go 1.20 added experimental arena support (golang.org/x/exp/arena). Arenas allow batch-freeing objects without GC pressure.


Step 8: Capstone — HTTP Server with Full Pooling


Summary

Pattern
Allocs/op
Use Case

No pool

2

Simple, low-traffic code

sync.Pool

1

High-throughput, GC-friendly

Channel pool

0

Permanent, survives GC

Arena

0 (bulk)

Request-scoped bulk alloc

Key Takeaways:

  • sync.Pool cuts allocations and GC pressure significantly

  • Always Reset() objects before returning to pool

  • sync.Pool is cleared on GC — don't store important state

  • Use -benchmem to measure allocs/op, not just ns/op

  • Arenas are ideal for request-scoped allocations (parse, serialize, free)

Last updated