When we think of memory leaks, we usually imagine something obvious like forgetting to close a file or a goroutine running forever. But in Go, memory leaks can be much more subtle.
One such case comes from how slices work under the hood.
How slices actually work:
A slice in Go is not the actual data. It’s just a small structure that contains:
a pointer to the underlying array
a length
a capacity
so when you reslice a slice, you're not making a new copy. You're just creating another view over the same underlying array.
The problem: memory being held unnecessarily:
Let’s say you’re reading a large file into memory and want to extract only the first group of digits from it. You might write a function like this:
var digitRegexp = regexp.MustCompile("[0-9]+")
func FindDigits(filename string) []byte {
b, _ := ioutil.ReadFile(filename)
return digitRegexp.Find(b)
}
At first glance, this looks fine. It reads the file, finds the first digit sequence, and returns it.
But here’s the issue: the returned slice still points to the original array that contains the entire file. So even though you’re only using a few bytes, the garbage collector can’t release the rest of the file from memory because it’s still being referenced by the slice.
This can easily lead to memory being held longer than necessary.
The fix: copy what you need:
To prevent this, you can simply copy the slice that you actually care about into a new one. That way, the rest of the file is no longer referenced and can be cleaned up.
func CopyDigits(filename string) []byte {
b, _ := ioutil.ReadFile(filename)
b = digitRegexp.Find(b)
c := make([]byte, len(b))
copy(c, b)
return c
}
This way, only the required bytes are kept in memory, and the rest of the file can be garbage collected.
This is a small detail that’s easy to overlook, but it can have a big impact especially in long running programs or memory heavy operations.
Top comments (0)