Go 2025-10-13 · 15 min 阅读

Go内存模型与分配机制


在操作系统中,存在寄存器、高速缓存、内存和磁盘,越接近cpu存储的容量越小,其对应的价格就越高昂。页表、分页管理等机制来减少内存碎片。

Golang中的内存模型,以空间换时间,一次缓存,多次复用。堆mheap正是基于该思想,产生的数据结构。依次细化粒度,建立了 mcentral、mcache 的模型,下面对三者作个梳理:

内存单元mspan

page和mspan的2个概念,page:最小的存储单元,默认大小为8KB,mspan大小为page的整数倍,且从8B到80KB 被划分为67种不同的规格,对应源代码在runtime/sizeclasses.go,mspan具有如下特点:

线程缓存mcache

中心缓存mcentral

要点:

全局堆缓存mheap

对象分配流程

不论是以下哪种方式,最终都会殊途同归步入mallocgc方法中,例如:new(T)、&T{}、make(xxxx),Golang中,依据object的大小,会将其分为下述三类:tiny微对象(0, 16B)、small小对象(16B,32KB)、large大对象(32KB,正无穷). 对于微对象的分配流程:

  1. 从P专属mcache的tiny分配器取内存(无锁)
  2. 根据所属的spanClass,从P专属mcache缓存的mspan中取内存(无锁)
  3. 根据所属的spanClass从对应的mcentral中取mspan填充到mcache,然后从mspan中取内存(spanClass粒度锁);
  4. 根据所属的spanClass,从mheap的页分配器pageAlloc取得足够数量空闲页组装成mspan填充到mcache,然后从mspan中取内存(全局锁);
  5. mheap`向操作系统申请内存,更新页分配器的索引信息,然后重复(4);

对于小对象的分配流程是跳过(1)步,执行上述流程的(2)-(5)步; 对于大对象的分配流程是跳过(1)-(3)步,执行上述流程的(4)-(5)步.

Go垃圾回收原理

做Java的都对GC比较熟悉,在JVM中常见的GC算法有:标记整理(Mark-Sweep)、标记压缩(Mark-Compact)、半空间复制(类似于G1),通过引用计数寻找不可达对象,便于垃圾回收。

回收算法

  1. Go V1.3之前是标记清除(mark and sweep)算法,步骤如下:
    • 第一步,先暂停程序业务逻辑,找出不可达对象和可达对象。
    • 第二步,开始标记,程序找出它所有可达的对象,并做上标记。
    • 第三步,标记完了之后,然后开始清除未标记的对象。
    • 第四步,停止暂停,让程序继续跑,然后循环重复这个过程,直到process程序声明周期结束。

标记-清除(mark and sweep)方法的缺点有: 1) stop the world, 让程序暂停,程序出现卡顿(重要问题);2)标记需要扫描整个heap;3)清除数据会产生heap碎片;

  1. Go V1.5引入的三色标记法,对象分为三种颜色标记:黑、灰、白,黑对象代表,对象自身存活,且其指向对象都已标记完成;灰对象代表,对象自身存活,但其指向对象还未标记完成;白对象代表,对象尙未被标记到,可能是垃圾对象。
    • 第一步,就是只要是新创建的对象,默认的颜色标记都是”白色”;
    • 第二步,每次GC回收开始,然后从根节点开始遍历所有对象,把遍历到的对象从白色集合放入”灰色”集合;
    • 第三步,遍历灰色集合,将灰色对象应用的对象从白色集合放入灰色集合,之后将此灰色对象放入黑色集合;
    • 第四步,重复第三步,直到灰色中无任何对象;

三色标记法的不足之处,如果三色标记法不被STW保护,下面2个条件满足,就会出现对象丢失的现象:

强弱三色不变式,破坏条件1和条件2,阻止引用对象丢失。

  1. 屏障机制,不足之处有,在结束时需要STW来重新扫描栈,大约需要10~100ms。插入屏障,对象被引用时触发的机制(栈空间对象不触发):
    • 具体操作,在A对象引用B对象的时候,B对象被标记为灰色。(将B挂在A下游,B必须被标记为灰色,无论B当前时什么颜色)。满色,强三色不变式(不存在黑色对象引用白色对象的情况了,因为白色对象会强制变成灰色)。
    • 场景1: A添加下游对象(nil, B) // A之前没有下游,新添加一个下游对象B,B被标记为灰色。
    • 场景2:A添加下游对象(C, B) // A将下游C更换为B,B被标记为灰色。

为避免栈上的对象被遗漏,在堆标记完成。准备回收白色对象前,会重新扫描一次栈(栈上所有对象都被标记为白色),另外,会加STW暂停保护栈,防止外界干扰。

删除屏障,对象被删除时触发的机制,具体操作:被删除的对象,如果自身为灰色或白色,那么被标记为灰色。

  1. 混合写屏障操作 Go V1.8引入的三色标记法 + 混合写屏障机制,具体操作如下,变形的弱三色不变式(结合了插入、删除写屏障两者的特点):
    • 1)GC开始将栈上的对象全部扫描并标记为黑色(之后不再进行第二次重复扫描,无需STW);
    • 2)GC期间,任何栈上创建的新对象,均为黑色;
    • 3)被删除的对象标记为灰色,被添加的对象标记为灰色;

场景1,对象被一个堆对象删除引用,成为栈对象的下游。在混合模式下,栈上的对象也不需要STW了。

场景2,对象被一个栈对象删除,成为另一个栈对象的下游。

场景3,对象被一个堆对象删除引用,成为另一个堆对象的下游。

场景4,对象从一个栈对象删除引用,成为另一个堆对象的下游。

# Go