A service had been quietly putting on weight for a fortnight. RSS climbing a few megabytes an hour, dead flat, no spikes, no correlation with traffic. The sort of leak that never pages you, it just eventually gets OOM-killed at three in the morning and restarts clean, so the graph saws gently upward and nobody panics. I finally got annoyed enough to chase it.
In Go the first stop is pprof. I had the heap profile endpoint already wired in, so it was just go tool pprof http://localhost:6060/debug/pprof/heap and then top to see who was holding the allocations. The answer was unambiguous and slightly embarrassing: nearly all of it sat under one function, the one that populated an in-memory map I'd added months ago as a "temporary" dedup cache.
The map was keyed by request ID. Every incoming request added an entry. Nothing ever removed one. I had written the half that fills the cache and simply never written the half that empties it. There was no eviction, no TTL, no size cap, nothing. It was not a cache, it was a list that pretended to be a cache and grew forever.
The fix was three lines and a lesson. A bounded map with a periodic sweep that drops entries older than five minutes, which is all the dedup window ever needed to be. RSS now sits flat as a board.
The leak hid for so long precisely because it was boring. No clever concurrency, no cgo, no finaliser nonsense. Just a programmer who added to a collection and never thought about the other end of its life. If you keep a map around for the lifetime of the process, the only real question is what takes things out of it, and "nothing" is always the wrong answer.