Go Memory Management — Find the Bug¶
Bug 1 🟢 — Sub-Slice Pinning¶
Solution
**Bug**: subslice keeps backing array alive. **Fix** — copy:Bug 2 🟢 — sync.Pool Without Reset¶
var pool = sync.Pool{New: func() any { return new(Buffer) }}
func use() {
b := pool.Get().(*Buffer)
defer pool.Put(b)
// use b; pool.Put without reset → next user sees old data
}
Solution
**Bug**: previous user's data leaks to next user. **Fix**:Bug 3 🟡 — Goroutine Leak¶
Solution
**Bug**: goroutine never exits; pins `req.Body` forever. Memory grows linearly with request count. **Fix** — context cancellation:Bug 4 🟡 — Map Doesn't Shrink¶
cache := map[int]string{}
for i := 0; i < 10_000_000; i++ {
cache[i] = "..."
}
for i := 0; i < 10_000_000; i++ {
delete(cache, i)
}
// Memory stays high; bucket array doesn't shrink
Solution
**Bug**: deleted entries free their values but bucket array stays at peak size. **Fix** — recreate:Bug 5 🟡 — Manually Triggering GC¶
Solution
**Bug** (kind of): unnecessary. The runtime decides. Forcing GC adds CPU overhead without benefit. **Fix**: remove `runtime.GC()`. Trust the runtime. Exception: deterministic testing or before sensitive measurements.Bug 6 🔴 — Memory Limit Not Set in Container¶
Solution
**Bug**: GC default behavior may allow heap to grow until OS kills the container. **Fix** — set GOMEMLIMIT: Or programmatically: GC runs more aggressively as heap approaches the limit.Bug 7 🔴 — Pool Holds State Beyond GC¶
var statePool = sync.Pool{New: ...}
state := statePool.Get()
// expecting state to be the previous instance
Solution
**Bug**: `sync.Pool` may discard entries at GC. `Get` may return a fresh `New()` even if you Put one moments ago. **Fix**: don't rely on Pool for state retention. Use it only for ephemeral object reuse.Bug 8 🔴 — Heap Profile Without Sampling¶
Solution
**Bug**: setting to 1 records EVERY allocation. Massive overhead. Profile becomes useless under load. **Fix**: use default (`512 * 1024` = 512 KB sampling) or `runtime.MemProfileRate = 0` to disable profiling temporarily.Bug 9 🔴 — Stack Overflow from Deep Recursion¶
func recurse(n int) {
var local [1024]int
if n > 0 { recurse(n - 1) }
_ = local
}
recurse(10_000_000)
Solution
**Bug**: each call uses 8 KB of stack. 10M calls = 80 GB. Exceeds 1 GiB default stack limit. Crash. **Fix** — convert to iterative:Bug 10 🔴 — runtime.GC() Doesn't Reduce RSS¶
Solution
**Bug**: GC frees heap memory but doesn't necessarily return it to the OS. **Fix** — explicit scavenging: `FreeOSMemory` returns unused pages to the OS. Use sparingly; can hurt performance.In this topic
Modes