这是我参与「第五届青训营 」伴学笔记创作活动的第 4 天
Go内存管理 | 青训营笔记
自动内存管理
首先,管理的是动态内存(根据需求动态分配的内存)
自动内存管理(垃圾回收, garbage collection,简称gc):由程序语言的运行时系统管理动态内存,避免手动管理,保证内存使用的正确性和安全性。
三个任务
- 为新对象分配空间
- 找到存活对象
- 回收死亡对象
追踪垃圾回收
- 标记根对象:静态变量、全局变量、常量等
- 标记可达对象:从根对象出发,找指向关系的传递
- 清理不可达对象:
- 存活对象复制到另外内存空间
- 死亡对象标记为可分配
- 移动并整理存活对象(类似于copy只不过没有额外空间只能原地复制) 对象被回收条件:指针指向关系不可达对象
分代GC(generational gc)
分代假说:很多对象分配完很快就不再使用了
每个对象都赋予年龄(经历过gc的次数),对年轻和老年的对象制定不同的gc策略,不同年龄放在堆的不同区域
- 年轻代:存活对象少,用复制做清理
- 老年代:一直活着,反复复制开销大,用mark-sweep collection
引用计数
- 每一个对象都有一个与之关联的引用数目
- 对象存活条件,当且仅当应用数大于0
优点
- 可以在程序执行过程中进行引用计数
- 不需要了解runtime细节
缺点 - 维护开销大:因为对象可能被多个协程引用,所以需要进行原子操作
- 内存开销大
- 无法回收引用环
Go内存分配
目标:为对象在堆上分配内存
做法:将内存分块,根据对象大小找一个最合适的块返回
缓存
编译器和静态分析
静态分析:不执行代码,推导程序行为,分析程序性质
控制流:程序执行流程
数据流:数据在控制流上传递
过程内分析:仅在函数内部进行分析
过程间分析:考虑函数调用时的函数传递和返回值的数据流和控制流。比较复杂,需要同时考虑数据流和控制流。
Go编译器优化
函数内联
内联:把函数体副本直接替换到调用位置上。
优点:减少了传递参数,保存寄存器等开销。同时把过程间分析转化为过程内分析。大多数情况是正向优化。
缺点: 函数体变大
Go语言特性导致内联受到较多限制,且内敛策略较多。
逃逸分析
函数内联使更多对象不逃逸
优化:未逃逸对象在栈上分配,减少堆上分配,降低gc负担