tmcalloc
参考链接
分配内存策略:全局缓存堆 + 进程私有缓存
- 对于小容量的内存申请,优先尝试进程私有缓存,若私有缓存不足,则向全局缓存申请。@H_403_9@
- 对于大容量的内存申请,直接向全局缓存申请。@H_403_9@
- 进程私有缓存:单链表数组,默认分配86个大小不同的块,每个块上的数组使用才初始化。@H_403_9@
- 全局缓存堆:单链表数组,一共会分配256个不同大小的块,1 page = 4k,链表对应数组每个元素page递增。@H_403_9@
- span:一个span可以包含几个连续分页,span的状态:未分配、作为大对象分配、作为小对象分配。@H_403_9@
golang 内存管理
和tcmalloc的区别:
- 局部缓存不针对进程或线程,而是分配给P(goroutine)@H_403_9@
golang的gc会stop the world
golang 变量分配:
@H_403_9@小内存变量直接分配在栈,若内存不够则在堆分配
@H_403_9@- 小内存若在函数中被返回,有可能分配在堆@H_403_9@
- 大内存变量直接在堆分配@H_403_9@
内存关键数据结构:
- mcache:goroutine cache,可以认为是 local cache。@H_403_9@
- mcentral:全局cache,mcache 不够用的时候向 mcentral 申请。@H_403_9@
- mheap:当mcentral 也不够用的时候,通过 mheap 向操作系统申请。@H_403_9@
mcache:
mcentral:
mheap:
对象分配内存的主要流程:
- object size > 32K,则使用 mheap 直接分配。 @H_403_9@
- object size < 16K,直接使用mcache直接分配。@H_403_9@
- object size > 16K && object size < 32K,先使用 mcache 中对应的 size class 分配。 如果 mcache 对应的 size class 的 span 已经没有可用的块,则向 mcentral 请求。 如果 mcentral 也没有可用的块,则向 mheap申请,并切分。 如果 mheap 也没有合适的 span,则想操作系统申请。@H_403_9@
内存回收流程:
- mcache 归还内存分两部分:归还mcentral内存,可能涉及锁竞争;除此之外,归还到mheap,直接插入链表头。@H_403_9@
- mcentral 归还mheap。@H_403_9@
- mheap 定时归还系统内存。@H_403_9@
各个版本的垃圾回收机制:
- v1.1 STW@H_403_9@
- v1.3 Mark STW,Sweep 并行@H_403_9@
- v1.5 三色标记法@H_403_9@
- v1.8 hybrid write barrier@H_403_9@
触发gc条件:
- 主动gc和被动gc(分配大小超过32k的对象)@H_403_9@
gc机制:
三色标记:
- 初始化:所有对象最开始都是白色。@H_403_9@
- 遍历可达对象:从 root 开始找到所有可达对象,标记为灰色,放入待处理队列。@H_403_9@
- BFS:遍历灰色对象队列,将其引用对象标记为灰色放入待处理队列,自身标记为黑色。@H_403_9@
- 处理完灰色对象队列,执行清扫工作。@H_403_9@
具体实现:
- 从root对象开始遍历,包括全局root指针和goroutine对象指针@H_403_9@
- mark:1. 从root指针遍历其余对象;2. 因为mark执行和用户程序并行,需要记录下mark期间新分配的对象,write barrier + re-scan@H_403_9@
- Stop The World 有两个过程:1. GC 将要开始的时候,这个时候主要是一些准备工作,比如 enable write barrier。2. 上面提到的 re-scan 过程。如果这个时候没有 stw,那么 mark 将无休止。@H_403_9@
- 清理速度提升:span可标示分配的对象,未被span标示的可被回收。@H_403_9@