1 自动内存管理
1.1 相关概念
- Mutator:业务线程,分配新对象,修改对象指向关系
- Collector:GC线程,找到存活对象,回收死亡对象的内存空间
- Serial GC:只有一个collector(需要暂停)
- Parallel GC:支持多个collectors同时回收的GC算法(需要暂停)
- Concurrent GC:mutator(s)和collector(s)可以同时执行(不需要暂停)
1.2 追踪垃圾回收
对象被回收的条件:指针指向关系不可达的对象
标记可达对象,清理所有不可达对象
1.3 分代GC
很多对象在分配出来后很快就不再使用了
对年轻和老年的对象,制定不同的GC策略,降低整体内存管理的开销
1.4 引用计数
存活条件:当且仅当引用数大于0
2 Go内存管理及优化
2.1 Go内存分配
提前将内存分块。首先向OS申请一大块内存,然后将内存划分为若干个大块,再将大块划分成特定大小的小块用于对象分配,有的大块用来分配不包含指针的对象(GC不需要扫描),有的大块分配包含指针的对象(GC需要扫描)。
缓存策略。每个P包含一个缓存用于为P上的G分配对象,如果缓存分配完毕,向下一级缓存申请未分配的大块。
2.2 Go内存管理优化
对象分配中,小对象占比较高
2.3 优化方案:Balanced GC
每个G都绑定一大块内存,称作GAB,用于无指针小对象分配。使用三个指针维护GAB:base、end、top
带来的问题:内存被延迟释放
解决办法:移动GAB中存活的对象到另一个GAB中,然后释放原GAB。
3 编译器和静态分析
3.1 编译器的结构
3.2 静态分析
不执行程序代码,推导程序的行为,分析程序的性质。
控制流:程序执行的流程
数据流:数据在控制流上的传递
3.3 过程内分析和过程间分析
过程内分析:仅在函数内部进行分析
过程间分析:考虑过程调用时参数传递和返回值的数据流和控制流
过程间分析是个难点问题,因为需要同时分析控制流和数据流
4 Go编译器优化
4.1 函数内联
将被调用函数的函数体的副本替换到调用位置上,同时重写代码以反映参数的绑定
优点:
- 消除函数调用开销
- 将过程间分析转化为过程内分析
缺点:
- 函数体变大,icache不友好
- 编译生成的Go镜像变大
4.2 Beast Mode
调整函数内联的策略,使更多函数被内联,更多对象不逃逸,可以分配在栈上
4.3 逃逸分析
分析代码中指针的动态作用域:指针在何处可以被访问
若发现指针p在当前作用域s:
- 作为参数传递给其他函数
- 传递给全局变量
- 传递给其他的goroutine
- 传递给已逃逸的指针指向的对象
则指针p指向的对象逃逸出s,反之则没有逃逸