A goroutine is a user-space thread—a lightweight execution context managed entirely by Go's runtime, not the OS kernel.
type g struct {
stack stack // current stack bounds
stackguard0 uintptr // for stack growth checks
m *m // current M (OS thread) running this G
sched gobuf // saved registers (PC, SP) for context switch
atomicstatus uint32 // runnable, running, waiting, etc.
// ... ~40 more fields
}
Each goroutine is just a struct (~2KB) containing:
Every CPU has registers—tiny, ultra-fast storage locations. Two are critical for execution:
| Register | Name | What It Holds |
|---|---|---|
| PC | Program Counter | Address of the next instruction to execute |
| SP | Stack Pointer | Address of the top of the current stack |
Memory Layout (simplified)
─────────────────────────
│ Code (.text) │ ← PC points somewhere here
─────────────────────────
│ ... │
─────────────────────────
│ Stack │ ← SP points to top
│ ┌─────────────────┐ │
│ │ local var a │ │
│ │ local var b │ │
│ │ return address │ │
│ └─────────────────┘ │
─────────────────────────
PC tells the CPU "where am I in the code?"
SP tells the CPU "where is my stack data?"
When the scheduler switches from running Goroutine A to Goroutine B, it must: