这是我参与「第三届青训营-后端场」笔记创作活动的的第2篇笔记
语言进阶-内存管理
一些概念:
- Mutator:业务线程,分配新对象,修改指向关系
- Collector:GC线程,寻找回收垃圾
- Serial:单个collector
- Parallel GC:支持多个collectors并行
- Concurrent GC:mutators与collectors可以同时执行,collectors须感知对象指向关系的改变
- Copying GC:将存活的对象复制到另外的空间
- Mark-sweep GC:将死亡的对象标记为可分配
- Mark-compact GC 移动并整理存活对象
分代GC
分代假说:很多对象分配后很快不再使用,对不同年轻、年老的对象执行不同的GC策略。
年轻代:常规的对象分配,存活对象少,可采用copying collection,GC吞吐率高
老年代,对象倾向于一直活着,反复复制开销大,采用mark-sweep collection
GO内存分块
GO为目标分配内存时,首先系统调用mmap(),申请内存,将内存划分成大块,称作msapn,将大块划分成特定大小的小块,用于对象分配。noscan/scan mspan:分配不包含/包含指针的对象,GC不需要/需要扫描。再根据对象大小选择适合的块返回。
缓存
这部分跟java不一样,每个线程M被分配一个处理器P,一次处理一个goroutine,mcache为每个goroutine分配各自的内存,mcache分配完毕,向mcentral申请有未分配块的mspan,当mspan中没有分配的对象,会被缓存在mcentral中,而不是立刻归还os。
优化
每秒GB级别的分配
优化方案:
Balanced GC:每个g绑定一大块内存1KB,goroutine allocation buffer。nonsacn类型对象分配<128B,三个指针维护GAB: base, end, top。将多个小对象的分配合并成一次大对象的分配。GAB总大小超过一个阈值,将其中存活的对象复制到其他GAB中,即cpying GC。
Beast mode
逃逸分析,函数内联拓展了函数边界,更多对象不逃逸,未逃逸的对象可以在栈上分配。类似于C的inline,go:noinline 告诉编译器不要进行内联优化。