Skip to content

Instantly share code, notes, and snippets.

@nsrip-dd
Last active October 23, 2025 19:36
Show Gist options
  • Select an option

  • Save nsrip-dd/39fa3bbd20439e07a1abf4de709741fd to your computer and use it in GitHub Desktop.

Select an option

Save nsrip-dd/39fa3bbd20439e07a1abf4de709741fd to your computer and use it in GitHub Desktop.
Pathological case for blowing up heap profile bucket sizes

Example run:

% go1.22.6 run .
29764202
% go version
go version go1.23.1 darwin/arm64
% go run .
3001476733

That's an over 100x increase.

package main
import (
"fmt"
"math/rand"
"runtime"
"runtime/metrics"
)
//go:noinline
func garbage() []byte {
//return make([]byte, rand.Intn(32))
return make([]byte, 4)
}
//go:noinline
func left(depth int) {
if depth == 0 {
garbage()
return
}
if depth < 16 {
// Keep the leaf end of the stack fixed to blow up the difference
// with having deeper stacks capture more recursion further up.
left(depth - 1)
return
}
if rand.Intn(2) == 0 {
left(depth - 1)
} else {
right(depth - 1)
}
}
//go:noinline
func right(depth int) {
if depth == 0 {
garbage()
return
}
if depth < 16 {
right(depth - 1)
return
}
if rand.Intn(2) == 0 {
left(depth - 1)
} else {
right(depth - 1)
}
}
func main() {
runtime.MemProfileRate = 1
defer func() {
s := make([]metrics.Sample, 1)
s[0].Name = "/memory/classes/profiling/buckets:bytes"
metrics.Read(s)
fmt.Printf("%v\n", s[0].Value.Uint64())
// "Read" the profile so profiling doesn't get stripped.
// These profiles get big so actually serializing them is really slow.
runtime.MemProfile(nil, false)
}()
for range 10000000 {
depth := 128
if rand.Intn(2) == 0 {
left(depth)
} else {
right(depth)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment