Go: Memory Allocation 内存分配
0.1 · Overview#
Runtime manages memory allocation with a multi-layer caching strategy to reduce lock contention and fragmentation. The allocator is inspired by TCMalloc.
Memory States (runtime/mem.go):
- None - Default state of system memory
- Reserved - Owned by runtime, not accessible
- Prepared - ?
- Ready - Safely accessible
0.2 · Allocation Architecture#
0.2.1 · Hierarchy: mcache → mcentral → mheap → OS#
Small Objects (< 32KB):
- Round up to a size class (1B ~ 32KB, ~70 classes)
- Check P’s mcache for available mspan - no lock required
- If empty, fetch from mcentral (global, per-size class) - requires lock
- If mcentral is empty, fetch from mheap
- If mheap is insufficient, request from OS (at least 1MB via syscall)
Large Objects (> 32KB):
- Bypass mcache and mcentral, allocate directly from mheap
0.2.2 · Size Classes#
Generated by mksizeclasses.go with 12.5% granularity between levels.
Classification:
- Tiny (< 16 bytes, noscan): Multiple tiny allocations share a block
- Small (16 bytes ~ 32 KB, or has pointer): Per-size-class span from mcache
- Large (> 32 KB): Direct allocation from mheap
Span Strategy:
- ~70 size classes (8 bytes ~ 32 KB)
- Each size has two variants: scan (contains pointers) and noscan
- Reduces fragmentation through span-based allocation
0.2.3 · Multi-Layer Caching#
Motivation: Reduce lock contention and allocation overhead
-
mcache (Per-P, lock-free)
- Each logical processor P maintains local mspan cache for each size class
- No locking required for allocation
-
mcentral (Global, fine-grained locks)
- ~67 × 2 mspan lists (per size class)
- Backs up mcache
-
mheap (Global, coarse-grained lock)
- Maintains free contiguous pages (via treap structure since Go 1.11)
- Pages are merged when returned (coalescence)
0.2.4 · Span Reclamation#
When returning a span to mheap:
- If span is still allocated (contains live objects), place in mcentral for that size class
- If span is fully freed, return pages to mheap
- mheap merges contiguous free pages (treap structure)
- Periodic background goroutine returns excess memory to OS
0.3 · Virtual Memory Layout#
Arena-based hierarchy (64-bit: 64MB per arena, 32-bit: 4MB):
Memory Space
↓
Arenas (64MB each on 64-bit)
├── heapArena (metadata for this arena)
│ ├── Bitmap (marks for all words in arena)
│ ├── Spans (page-to-span mapping for all pages in arena)
│ └── Additional metadata
└── mheap._arenas (2D mapping: L1 × many L2s)
Each arena is aligned, allowing efficient mapping via array indexing.
0.4 · Stack Allocation#
Multi-layer and multi-class strategy similar to heap:
- Reduces allocation lock contention
- Minimizes stack waste
0.5 · Memory Reclamation#
- On-demand: When goroutine needs new span, it first attempts to reclaim memory by sweeping spans of same size
- Background: sysmon periodically returns excess mheap memory to OS
0.5.1 · Sweep Mechanics#
When goroutine needs allocation:
- For small span: Sweep same-size spans until at least one object is freed
- For large span: Sweep spans until at least that many pages are freed to heap
- Edge case: Sweeping may release non-contiguous single-page spans, still leaving gaps for larger allocations
0.6 · References#
- https://povilasv.me/go-memory-management/
- https://povilasv.me/go-memory-management-part-2/
- https://povilasv.me/go-memory-management-part-3/
- https://draveness.me/golang/docs/part3-runtime/ch07-memory/golang-memory-allocator/
- http://t.zoukankan.com/zpcoding-p-13259943.html
- https://nghiant3223.github.io/2025/06/03/memory_allocation_in_go.html