Go

How can subslicing cause memory leaks, and how do you fix them?

Question 49HardGo 1.22 to 1.25

A subslice keeps the entire backing array reachable, not just the part you can see. If you read a 10 MB file and keep data[:16] in a long-lived cache, all 10 MB stays pinned. Substrings have the same problem: s[a:b] shares the original string's bytes.

// Leaky: header keeps the whole 10MB array alive
func magic(path string) []byte {
	data, _ := os.ReadFile(path)
	return data[:4]
}

// Fixed: copy the bytes you need
func magicFixed(path string) []byte {
	data, _ := os.ReadFile(path)
	return slices.Clone(data[:4]) // or bytes.Clone / append([]byte(nil), ...)
}

// Strings: same problem
func firstToken(line string) string {
	tok, _, _ := strings.Cut(line, " ")
	return strings.Clone(tok) // Go 1.20: detaches from the big line
}

A related leak: pointers past len. After s = s[:len(s)-1] on a []*Big, the removed element is still in the backing array, so the GC cannot free it. Zero it first:

s[len(s)-1] = nil
s = s[:len(s)-1]

What interviewers look for: knowing that the GC tracks objects (the whole array), not ranges, and knowing to use pprof heap profiles (inuse_space) to find these leaks.

More on Arrays, Slices, Maps & Strings

All 37 Arrays, Slices, Maps & Strings questions