内存管理
内存管理
内存管理-内存分配
分配器
线性分配器
空闲链表分配器
go内存管理
- 主要指堆、栈内存的分配和回收
- 借鉴了TCMalloc内存分配思想:缓存、分级分配
- 增加了逃逸分析、gc
go内存分配组成部分
- page:内存被划分成大小不等的页,Go 选择 8KB page
- span(跨度):内存管理的基本单位,一组连续的page组成一个span,GC 是按 span 扫描的
- mcache:类似TCMalloc的线程缓存,go的每个P挂载一个mcache,可以无锁访问
- mcentral:类似TCMalloc的中心缓存,线程共享,需要加锁访问
- mheap:与TCMalloc中的PageHeap类似,也需要加锁访问
go Tiny、小对象(32KB)分配
- 1、计算对象所需要的内存大小
- 2、跟进转化表,找出所属的span(跨度)
- 3、从span中分配对象空间,按照隔离适应的方式
- 4、优先从mcache中的span分配,若不够,从mcentral中申请span
- 5、若mcentral中也不够,向mheap申请
- 6、mheap向os申请
go 大对象分配
- 直接向mheap申请
参考
- https://draveness.me/golang/docs/part3-runtime/ch07-memory/golang-memory-allocator/
- https://segmentfault.com/a/1190000020338427
- https://blog.csdn.net/aaronjzhang/article/details/8696212
内存管理-垃圾回收
标记-清除(v1.26前)
- 1、初始状态,所有的对象都是白色
- 2、从根对象开始扫描,将引用的对象标记成灰色
- 3、分析灰色对象是否引用了其他对象,若无,标记自己成白色;若有,将自己标记成黑色,将引用对象标记成灰色
- 重复 2、3 步骤,直到所有的对象都变成黑或白。白色对象即是要被回收的垃圾
- 写屏障
- 作用是:防止用户程序修改对象引用导致 GC 漏扫描对象
- 原理:gc和用户程序并行,这个过程会修改内存引用,写屏障会将被引用对象从白色(待 GC 对象)标记成灰色(被引用,未扫描)
- 辅助GC
- 如果GC的速度小于用户程序分配对象的速度,就会把用户程序暂停(STW)
Green Tea:按页扫描 + 向量化加速(v1.26后)
传统 标记-清除 算法按对象扫描,因为需要在不同 page 之间来回跳,mark 阶段占 GC 总开销约 90%。
工作原理: 
按页扫描:
- 1、每个对象增加两个 bit 记录对象元数据
Seen bits:是否有指针指向该对象
Scanned bits:是否已扫描该对象
- 2、从根对象出发,发现对象则把整页加入工作列表,而不是单个对象
- 3、关键;当发现页累积了多个对象需要扫描,则一次性扫描多个相邻对象(利用了 CPU 页缓存)
seen - scanned 差集,就是该 page 上所有待扫描的对象
向量化加速:
AVX-512 向量指令可以加速扫描过程,一次性扫描页上所有对象
GC触发条件
- 超过内存的阈值
- 到达特定的时间
- 手动GC
参考
- Green Te GC
- https://draveness.me/golang/docs/part3-runtime/ch07-memory/golang-garbage-collector/
- https://juejin.cn/post/6844903917650722829
逃逸分析
- 定义:go的内存分配由编译器完成,通过逃逸分析,决定内存分配是在栈上还是在堆上。若变量的生命周期是完全可知,则分配到栈上,否则分配到堆(逃逸)。
- 编译器尽可能地内存分配到栈,几种内存分配到堆得情况(逃逸)
- 变量类型不确定
- 函数内暴露给外部的指针
- 变量所占内存较大
- 变量的大小不确定
- 逃逸分析的作用:写出更好的程序,使内存尽可能地分配到栈,减小gc压力,减少内存分配开销。
- 如何查看:
go build -gcflags="-m" - 参考
- 逃逸分析:https://mp.weixin.qq.com/s/xhBVv6JEPY8R3kCJlbirYw
- 堆:https://www.jianshu.com/p/6b526aa481b1
内存分析
- 参考:https://www.oschina.net/translate/debugging-performance-issues-in-go-programs
This post is licensed under CC BY 4.0 by the author.





