这是我参与「第五届青训营 」伴学笔记创作活动的第 8 天
今天复习整理之前内存管理的部分课程
Mutator: 业务线程,分配新对象,修改对象指向关系
Collector: GC线程,找到存活对象,回收死亡对象的内存空间
Go Runtime
Serial GC: 只有一个 collector
Parallel GC: 并行 GC,支持多个 collectors 同时回收的GC算法
Concurrent GC: 并发 GC,支持 mutator(s)和collector(s)同时执行的GC算法,要求collectors必须感知对象指向关系的改变(即回收的同时程序还在运行,产生的新对象也要标记为相应的存活)
评价GC算法
安全性:不能回收存活的对象,基本要求
吞吐率(Throughput): 1-GC时间/程序总时间,即花在GC上的时间
暂停时间:stop the world (STW)业务是否感知
内存开销:GC元数据开销
追踪垃圾回收
对象被回收的条件:指针指向关系不可达的对象
标记根对象:静态变置、全局变量、常量、线程栈等
标记:找到可达对象,从根对象出发找到所有可达对象
清理:根据对象的生命周期,使用不同的标记和清理策略
将存活对象复制到另外的内存空间(Copying GC)
将死亡对象的内存标记为"可分配"(Mark-sweep GC)(类似c++)
移动并整理存活对象(Mark-compact GQ)(和coping gc类似,只不过是用同一个空间操作)
分代GC
对象的年龄:经历GC的次数
对年轻和老年对象,指定不同的GC策略,降低开销
年轻代:存活的少,可以采用coping gc
老年代:对象一直存活的多,复制开销大,可以采用mark-sweep gc
引用计数
优点:内存操作平铺到程序执行过程中,不需要复杂的机制,专注计数即可
缺点:维护引用计数开销大(要保证计数操作是原子操作),不好回收环形结构(引用计数都为1),计数本身占内存,回收复杂结构的引用计数时可能引发暂停