本文档描述了 Go 运行时中的内存管理系统。它涵盖了 Go 程序中内存的分配、组织和回收方式,重点关注堆分配系统、内存结构和分配机制。有关垃圾回收的具体信息,请参阅 垃圾回收。
Go 的内存管理旨在实现快速、并发,并最大限度地减少碎片。该系统结合使用了线程局部缓存、中央空闲列表和堆分配器,可在无需程序员手动干预的情况下高效地管理内存。
Go 内存管理的关键特性
来源:src/runtime/malloc.go5-76 src/runtime/mgc.go1-120
Go 的内存管理系统采用分层结构,在性能和内存效率之间取得平衡。
主要组件有:
mheap:主分配器,以页面粒度(8KB 页面)管理内存。它是所有内存的来源,并直接管理大块分配。
mspan:一个页面块,包含特定大小类对象的内存。每个 mspan 管理相同大小对象的内存。
mcentral:收集给定大小类的所有 span。当 mcache 需要更多特定大小类的内存时,它会从相应的 mcentral 获取。
mcache:每个 P(处理器)的 mspan 免费空间缓存。这使得大多数分配无需锁即可进行。
来源:src/runtime/malloc.go10-76 src/runtime/mheap.go56-180 src/runtime/mcache.go1-50 src/runtime/mcentral.go1-100
堆包括
Arena:固定大小的虚拟内存区域(64 位系统上为 64MB,32 位系统上为 4MB),与自身大小对齐。
Arena Map:一个两级索引,从虚拟地址映射到 arena 元数据,覆盖整个可能的地址空间。
heapArena:每个 arena 的元数据,包含
Spans:连续页面的运行,管理相同大小类的对象。
Objects:程序实际使用的分配内存。
来源:src/runtime/malloc.go77-254 src/runtime/mheap.go62-176 src/runtime/mbitmap.go5-54
Go 根据大小将分配分为不同的类别。
分配过程遵循以下步骤:
来源:src/runtime/malloc.go10-76 src/runtime/malloc.go650-800
Go 中主要的分配路径涉及以下关键函数:
mallocgc:内存分配的主要入口点。mcache.nextFree:从 P 的缓存中快速分配。mcache.refill:当 mcache 为空时,从 mcentral 获取新 span。mcentral.cacheSpan:为 mcache 提供 span。mheap.alloc:从堆中分配 span。来源:src/runtime/malloc.go800-1000
当对象被垃圾回收器释放时,内存并不会立即返回给操作系统。而是:
来源:src/runtime/mgcsweep.go1-58 src/runtime/mgcscavenge.go1-32
Go 使用整理机制将未使用的内存返还给操作系统。
整理程序会尝试在内存保留(以备将来使用)和 RSS(驻留集大小)减少之间取得平衡,使用基于近期分配模式的启发式方法。
来源:src/runtime/mgcscavenge.go5-54
Go 维护详细的内存统计数据,以指导分配、垃圾回收和整理决策。
这些信息决定了何时发生垃圾回收以及内存整理的积极程度。
来源:src/runtime/mgcpacer.go1-60
尽管本文档主要关注堆内存,但值得注意的是 Go 中的栈是:
栈增长是通过检测栈溢出并在需要时分配更大的栈来处理的。
每个 P(处理器)都有一个本地 mcache,其中包含每个大小类的空闲对象的 span。这使得大多数分配可以无需锁定即可进行,在常见情况下提供非常快速的分配。
mcache 代表内存分配层级结构的第一级,旨在最大限度地减少多线程程序中的争用。
当 mcache 中的 span 填满时,会从 mcentral 获取新的 span。
在最底层,Go 内存系统以页面(通常为 8KB)为单位管理内存。
页面分配器使用位图来有效地跟踪堆中空闲和已使用的页面。
来源:src/runtime/mpagealloc.go1-25 src/runtime/mheap.go20-55
Go 为 tiny 分配(≤16 字节,不含指针)进行了优化。
来源:src/runtime/malloc.go125-135
大于 32KB 的对象有不同的处理方式:
内存管理与垃圾回收器紧密集成。
有关垃圾回收工作原理的详细信息,请参阅 垃圾回收。
来源: src/runtime/mgc.go1-120 src/runtime/mbitmap.go5-20
Go 的内存管理在很大程度上是自动化的,但运行时提供了一些供高级使用的 API
runtime.MemStats:提供详细的内存统计信息runtime.GC():强制执行垃圾回收runtime.SetFinalizer():为对象设置一个终结器函数runtime.ReadMemStats():将 MemStats 结构体填充为当前的内存统计信息这些 API 应谨慎使用,因为运行时通常会在无需干预的情况下优化内存管理。