Lesson 23 · Testing, tooling & performance

The runtime: scheduler & GC

How Go runs millions of goroutines and reclaims memory — the finale.

Your win: explain the GMP scheduler and Go's concurrent garbage collector well enough to answer "how does Go run so many goroutines cheaply?" — the under-the-hood question that closes strong interviews.

The GMP scheduler

Goroutines aren't OS threads (Lesson 14). The Go runtime multiplexes many goroutines onto a few threads using three entities:1

G — Goroutine

Your concurrent task; a tiny growable stack. Cheap to create; millions are feasible.

M — Machine

An OS thread. It executes Go code only while holding a P; may block on syscalls.

P — Processor

A logical processor with a local run queue. There are exactly GOMAXPROCS of them (≈ CPU cores).

M:N scheduling Many goroutines (G) run on few OS threads (M), coordinated by GOMAXPROCS logical processors (P). Each P has a local run queue; when it empties, it steals work from another P. That's how Go gets massive concurrency with a handful of threads — and why launching a goroutine is nearly free.

Since Go 1.14 the scheduler is also preemptive: a goroutine running a tight loop can be interrupted so others get a turn (no more "one hot goroutine starves the rest"). A blocking syscall hands the P to another M so work continues.

The garbage collector

Go is garbage-collected: you allocate, the runtime reclaims. Its GC is a concurrent, tri-color mark-and-sweep collector tuned above all for low pause times — it runs alongside your program, so stop-the-world pauses are sub-millisecond, not the hundreds of ms of older GCs.2

Anchor — why our design is "free" Our Kafka engine spawns a goroutine per consumer instance, the fan-in code spawns per request, worker pools spawn N — and none of it is extravagant, precisely because GMP makes goroutines cheap and the concurrent GC reclaims their memory without long pauses. "Just launch a goroutine" is idiomatic because of this runtime.
Interview framing You don't manage the scheduler or GC by hand — that's the point. Know the model (GMP, concurrent low-pause GC), know the knobs (GOMAXPROCS, GOGC, GOMEMLIMIT), and know how to observe them (Lesson 22's profiles, the GC guide's trace tools). "I'd measure before tuning" beats "I'd set GOGC to X."
Read this next

A Guide to the Go Garbage Collector + "Getting to Go"

The official GC guide (mental model + tuning) and Rick Hudson's keynote on how the low-pause collector was built. For the scheduler, the runtime docs + the Scalable Scheduler design doc.

go.dev/doc/gc-guide · go.dev/blog/ismmkeynote
pkg.go.dev/runtime (GOMAXPROCS, GC knobs)

Check yourself (from memory)

Q1. In the GMP model, P is…

P = logical processor with a local run queue; there are GOMAXPROCS of them. G = goroutine, M = OS thread.

Q2. Go schedules goroutines using…

Many G onto few M via P, with per-P run queues and work-stealing — why goroutines are cheap.

Q3. Go's garbage collector is designed primarily for…

Concurrent tri-color mark-sweep tuned for sub-ms pauses, running alongside your program.
Why is it fine to spawn thousands of goroutines in our services?
recall, then click to reveal
The GMP scheduler multiplexes many goroutines (G) onto a few OS threads (M) via GOMAXPROCS logical processors (P) — M:N scheduling with per-P run queues and work-stealing. Goroutines have tiny growable stacks and switch in user space (no OS thread each). A concurrent, low-pause GC then reclaims their memory without long stop-the-world. So "just launch a goroutine" is idiomatic and cheap — the runtime handles it.
🎓 Course complete — all 23 lessons You've gone from Go's value model → methods, interfaces & generics → errors & idioms → the full concurrency pillar → testing, tooling & the runtime. Every concept is anchored to code you own. The remaining work isn't more reading — it's retrieval under pressure. Ask me to run a mixed mock interview across all five parts; where you're solid, I'll record it so we both know it's locked in.
Ready for the mock interview, or want to revisit any part in more depth (e.g. real profiling practice, or building a concurrency pattern from scratch)? Ask me.

1. runtime package (GOMAXPROCS); the Scalable Go Scheduler design doc; Concurrency is not Parallelism.

2. A Guide to the Go Garbage Collector; Getting to Go: The Journey of Go's Garbage Collector.