← notes
01 Aug 2026·Go

How Go's Garbage Collector Evolved

Recently, I started reading about Go’s Garbage Collector after coming across a Reddit thread mentioning that Go used to suffer from noticeable GC pauses — but that the problem had largely been solved.

That immediately made me curious:

What exactly were those pauses, and how did Go reduce them?

That question sent me down a rabbit hole.

I started exploring how heap management works, different garbage collection algorithms, and why a naïve Stop-The-World (STW) mark-and-sweep approach can introduce noticeable pauses.

Then I came across the tri-color marking approach.

Instead of completely stopping the application while the GC does its work, Go’s collector can perform much of the marking concurrently with the application. The idea of separating objects into white, grey, and black sets was probably the biggest conceptual shift for me.

And then there’s GOGC — a simple but powerful tuning knob that lets developers trade memory usage against GC CPU overhead depending on the workload.

The biggest aha moment for me was realizing how deeply a garbage collector can influence an application’s performance.

There isn’t one universally “best” GC strategy.

Some runtimes are willing to tolerate short stop-the-world pauses to maximize throughput, while others prioritize low latency, even if that means spending more CPU or memory to achieve it.

That trade-off made me think differently about runtime performance and language design.

What initially looked like a small implementation detail — “how does Go clean up unused memory?” — turned out to be a much deeper question about latency, throughput, memory, CPU, and the trade-offs between them.


References