GO语言内存管理 | 豆包MarsCode AI 刷题

35 阅读2分钟

GO语言内存管理

1. 协程栈

  • 协程栈记录了协程的执行现场,初始空间一般为 2KB
  • 协程栈还负责记录局部变量,传递参数和返回值。
  • GO 使用参数拷贝传递。
  • GO 协程栈位于 GO 堆内存上,堆内存位于操作系统虚拟内存上。
  • 栈空间不够的时候可以申请分段型(早期版本)、连续型新空间。

2. 逃逸分析:

  • 栈回收后需要继续使用的变量或者太大的变量,不能放在栈上,应该在堆上,这种现象称为逃逸分析
  • 指针逃逸:函数返回了参数的指针。
  • 空接口逃逸:如果函数参数为空接口,实参可能逃逸,因为空接口类型的函数往往会使用反射。
  • 大变量逃逸:一般超过 64KB 的变量就会发生逃逸。

3. Go堆内存结构

  • Go 模仿 TCmalloc,建立了自己的堆内存架构。
  • 使用 heapArena 向操作系统申请内存。
  • 使用 heapArena 时,以 mapan 为单位,防止碎片化。
  • mcentral 是 mspan 们的中心索引。
  • mcache 记录了分配给各个 P 的本地 mspan。

4. 堆内存分配

  • Go对象根据大小分为微对象,小对象和大对象。
  • 微小对象直接在本地 mspan 中分配,不够的话再交换新的 mspan。
  • 大对象直接从 heapArena 开辟 0 级的 mspan。

3. 三色标记法

  • Go 的垃圾回收采用标记-清除的方式

  • 三色:

    • 黑色:有用,而且已经扫描完成,不能清除
    • 灰色:有用,但是扫描还未完成,不能清除
    • 白色:还未扫描或者扫描结束后可以清除的对象
  • 过程:

    • 起初所有对象都是白色的
    • 从根出发扫描可达对象,标记为灰色
    • 扫描灰色对象,将其引用的对象标记为灰色,自身标记为黑色
    • 分析结束后,清除白色的对象
    • 再次标记时,所有对象恢复为白色
  • 并行标记:

    • 删除屏障:在释放某个对象的指针时候把引用的对象置为灰色,可以杜绝在GC标记中被释放的指针被清除
    • 插入屏障:对指针新指向的对象置为灰色,可以杜绝在GC标记中被插入的指针导致误删除
    • 混合屏障:Go 采用的方式,被删除和背添加的堆对象标记为灰色
  • 触发时机:

    • 系统定时触发:2分钟内没有GC时触发
    • 用户显式触发:用户调用 runtime.GC 方法
    • 申请内存触发:给对象申请空间的时候,可能触发
  • 优化:

    • 内存池化:缓存对象,防止频繁的创建和删除
    • 减少逃逸
    • 使用空结构体