全部学科
Python全栈
python
NodeJS全栈
nodejs
小程序首页
📅 2026-05-14 8 分钟 ✍️ juanwangdev

Go运行时内存管理

Go运行时采用分层内存管理,高效分配和复用内存。

内存分配层级

mcache → mcentral → mheap

Go
Goroutine申请内存
    ↓
mcache(本地缓存)← 无锁分配
    ↓ 缓存不足
mcentral(中央缓存)← 有锁获取
    ↓ 不足
mheap(堆)← 系统申请
    ↓
操作系统(mmap)

mcache实现无锁分配,小对象分配极其高效。

mcache本地缓存

结构与作用

Go
// 每个P拥有一个mcache
type mcache struct {
    // 按大小级别缓存Span
    alloc [numSpanClasses]*mspan
    // 微对象分配器
    tiny  uintptr
    tinyoffset uintptr
}

// 无锁分配小对象
func allocSmall(size) {
    span := mcache.alloc[sizeClass]
    return span.alloc()
}

Span级别划分

Go
对象大小 → Span级别(size class)

1-8字节   → class 1(8字节)
9-16字节  → class 2(16字节)
17-32字节 → class 3(32字节)
...
32768字节 → class 67

共67个级别,大对象直接从堆分配

小于32KB的对象从Span级别分配,大于32KB直接从堆分配。

mcentral中央缓存

结构与作用

Go
type mcentral struct {
    sizeclass  int        // 大小级别
    nonempty   mSpanList  // 有空闲Span列表
    empty      mSpanList  // 无空闲Span列表
}

// mcache不足时从mcentral获取
func cacheSpan() *mspan {
    // 从nonempty获取
    // 或从heap申请新Span
}

Span流转

Go
mcentral.nonempty(有空闲)
    ↓ 分配给mcache
mcentral.empty(无空闲)
    ↓ 回收后放回nonempty

mheap堆管理

结构与作用

Go
type mheap struct {
    arenas []arena  // 堆区域数组
    central [numSpanClasses]mcentral

    // Arena结构
    // 64MB(Linux)或4MB(Windows)
}

// 大对象或mcentral不足时
func allocSpan(size) *mspan {
    // 从空闲Arena分配
    // 或向系统申请新Arena
}

Arena划分

Go
堆由多个Arena组成
Linux:每个Arena 64MB
Windows:每个Arena 4MB

Arena进一步划分为Span
Span管理同大小对象

Span内存单元

Span结构

Go
type mspan struct {
    startAddr  uintptr    // 起始地址
    npages     uintptr    // 页数
    nelems     uintptr    // 对象数量
    allocBits  *gcBits    // 分配位图
    freeindex  uintptr    // 下一个空闲索引
}

// Span包含多个相同大小对象
// 通过位图标记已分配/空闲

Span分配对象

Go
// 从Span分配一个对象
func span.alloc() uintptr {
    // 查找allocBits空闲位
    index := freeindex

    // 计算地址
    addr := startAddr + index * elemsize

    // 更新位图
    allocBits.set(index)

    return addr
}

内存分配流程表

步骤层级锁竞争对象大小
1mcache无锁<32KB
2mcentral有锁mcache不足
3mheap全局锁>32KB或不足
4OS系统调用堆不足

对象大小分类

类别大小分配位置
微对象<16字节tiny allocator
小对象16-32KBmcache→Span
大对象>32KBmheap直接

tiny allocator

微对象分配

Go
// 极小对象(<16字节)使用tiny分配
type mcache struct {
    tiny       uintptr   // 当前tiny块地址
    tinyoffset uintptr   // tiny块内偏移
}

// 合多个小对象到同一16字节块
func allocTiny(size) {
    if tinyoffset + size <= 16 {
        // 从当前块分配
        tinyoffset += size
        return tiny + tinyoffset
    }
    // 申请新tiny块
}

tiny allocator合并多个极小对象,减少内存碎片。

内存复用机制

Span回收

Bash
// Span对象全部释放后
func sweepSpan(span) {
    if span.allocCount == 0 {
        // 放回mcentral
        mcentral.nonempty.insert(span)
    }
}

Span迁移

text
全部空闲 → 放回mcentral.nonempty
部分空闲 → 保留在mcache
完全满  → 等待sweep回收

内存统计

runtime.ReadMemStats

text
var m runtime.MemStats
runtime.ReadMemStats(&m)

fmt.Println("Alloc:", m.Alloc)       // 当前分配
fmt.Println("TotalAlloc:", m.TotalAlloc) // 累计分配
fmt.Println("Sys:", m.Sys)           // 系统申请
fmt.Println("HeapAlloc:", m.HeapAlloc) // 堆分配
fmt.Println("HeapSys:", m.HeapSys)   // 堆系统内存

MemStats主要字段

字段含义
Alloc当前使用内存
TotalAlloc累计分配内存
Sys从OS申请内存
HeapAlloc堆分配量
HeapSys申请系统内存
HeapIdle堆空闲内存
HeapInuse堆使用内存
HeapReleased释放给OS的堆内存
HeapObjects堆对象数量

内存碎片控制

tcmalloc思想

text
// Go借鉴tcmalloc设计
// 1. 分级Span减少碎片
// 2. 线程本地缓存减少锁
// 3. Span复用减少分配

// Span大小与对象大小匹配
// 8字节对象 → 8KB Span(1024个对象)
// 16字节对象 → 16KB Span(1024个对象)

碎片率控制

text
理论碎片率:
- 小对象:<10%
- 大对象:低碎片

实际碎片率:
- Span内碎片(最后一个对象未满)
- Span间碎片(部分空闲Span)

内存释放

heap释放

text
// 空闲内存归还OS
func scavenge() {
    if heapIdle > threshold {
        // madvise(DONTNEED)
        // 或munmap
        heapReleased += size
    }
}

内存延迟释放

text
GC后内存不立即归还OS
    ↓ 等待一段时间
scavenge周期检查
    ↓ 空闲超过阈值
释放给操作系统

默认延迟释放,避免频繁系统调用。

内存分配器调试

GODEBUG选项

text
# 查看内存分配器信息
GODEBUG=allocfreetrace=1

# 查看内存统计
GODEBUG=memprofile=mem.prof

# 运行内存分析
go tool trace mem.prof

要点总结

  • mcache本地缓存实现无锁分配
  • mcentral中央缓存补充mcache
  • mheap管理全局堆内存
  • Span是内存分配单元
  • 小对象(<32KB)走Span级别
  • 大对象(>32KB)直接从堆分配
  • tiny allocator处理极小对象
  • 分级管理减少内存碎片
  • ReadMemStats查看内存统计
  • 空闲内存延迟归还OS
  • 借鉴tcmalloc高效分配思想

📝 发现内容有误?点击此处直接编辑

← 上一篇 Go系统调用与网络轮询器
下一篇 → Go包管理与依赖管理
想查看更多题目和详细解析?
小程序提供完整的题库、模拟考试和详细解析
马上就来

长按或扫描二维码,立即体验

扫码体验小程序
马上就来
使用微信扫描二维码
立即体验完整题库