golang 内存管理 + 垃圾回收

tmcalloc

参考链接
分配内存策略:全局缓存堆 + 进程私有缓存

  • 对于小容量的内存申请,优先尝试进程私有缓存,若私有缓存不足,则向全局缓存申请。
  • 对于大容量的内存申请,直接向全局缓存申请。
  • 进程私有缓存:单链表数组,默认分配86个大小不同的块,每个块上的数组使用才初始化。
  • 全局缓存堆:单链表数组,一共会分配256个不同大小的块,1 page = 4k,链表对应数组每个元素page递增。
  • span:一个span可以包含几个连续分页,span的状态:未分配、作为大对象分配、作为小对象分配。

golang 内存管理

和tcmalloc的区别:

  • 局部缓存不针对进程或线程,而是分配给P(goroutine)
  • golang的gc会stop the world

    golang 变量分配:

  • 小内存变量直接分配在栈,若内存不够则在堆分配

  • 小内存若在函数中被返回,有可能分配在堆
  • 大内存变量直接在堆分配

内存关键数据结构:

  • mcache:goroutine cache,可以认为是 local cache。
  • mcentral:全局cache,mcache 不够用的时候向 mcentral 申请。
  • mheap:当mcentral 也不够用的时候,通过 mheap 向操作系统申请。

mcache:

  • 好处:内存分配不会涉及锁竞争。
  • 链表数组结构,如果内存不够时,会向mcentral申请。
  • mspam 数组进行管理。

mcentral:

  • 链表数组结构,如果内存不够时,会向mcentral申请。
  • 会涉及到锁竞争。
  • mspan list 管理。

mheap:

  • 在系统初始化的时候会堆mheap进行申请和初始化。
  • 如果内存不够,会向系统内存申请。

对象分配内存的主要流程:

  • object size > 32K,则使用 mheap 直接分配。
  • object size < 16K,直接使用mcache直接分配。
  • object size > 16K && object size < 32K,先使用 mcache 中对应的 size class 分配。 如果 mcache 对应的 size class 的 span 已经没有可用的块,则向 mcentral 请求。 如果 mcentral 也没有可用的块,则向 mheap申请,并切分。 如果 mheap 也没有合适的 span,则想操作系统申请。

内存回收流程:

  • mcache 归还内存分两部分:归还mcentral内存,可能涉及锁竞争;除此之外,归还到mheap,直接插入链表头。
  • mcentral 归还mheap。
  • mheap 定时归还系统内存。

各个版本的垃圾回收机制:

  • v1.1 STW
  • v1.3 Mark STW, Sweep 并行
  • v1.5 三色标记法
  • v1.8 hybrid write barrier

触发gc条件:

  • 主动gc和被动gc(分配大小超过32k的对象)

gc机制:

三色标记:

  • 初始化:所有对象最开始都是白色。
  • 遍历可达对象:从 root 开始找到所有可达对象,标记为灰色,放入待处理队列。
  • BFS:遍历灰色对象队列,将其引用对象标记为灰色放入待处理队列,自身标记为黑色。
  • 处理完灰色对象队列,执行清扫工作。

具体实现:

  1. 从root对象开始遍历,包括全局root指针和goroutine对象指针
  2. mark:1. 从root指针遍历其余对象;2. 因为mark执行和用户程序并行,需要记录下mark期间新分配的对象,write barrier + re-scan
  3. Stop The World 有两个过程:1. GC 将要开始的时候,这个时候主要是一些准备工作,比如 enable write barrier。2. 上面提到的 re-scan 过程。如果这个时候没有 stw,那么 mark 将无休止。
  4. 清理速度提升:span可标示分配的对象,未被span标示的可被回收。

猜你喜欢

转载自blog.csdn.net/qq_17612199/article/details/80278632