什么是垃圾回收:
Golang的垃圾回收(GC)就是将不再需要的内存块释放掉,否则不能回收的垃圾就是泄露的内存
垃圾回收算法
业界常见的垃圾回收算法有以下几种:
- 引用计数:对每个对象维护一个引用计数,当引用该对象的对象被销毁时,引用计数减1,当引用计数器为0时回收该对象。
- 优点:对象可以很快地被回收,不会出现内存耗尽或达到某个阀值时才回收。
- 缺点:不能很好地处理循环引用,而且实时维护引用计数,也有一定的代价。
- 代表语言:Python、PHP、Swift
- 标记-清除:从根变量开始遍历所有引用的对象,引用的对象标记为”被引用”,没有被标记的进行回收。
- 优点:解决了引用计数的缺点。
- 缺点:需要STW,即要暂时停掉程序运行。
- 代表语言:Golang(其采用三色标记法)
- 分代收集:按照对象生命周期长短划分不同的代空间,生命周期长的放入老年代,而短的放入新生代,不同代有不同的回收算法和回收频率。
- 优点:回收性能好
- 缺点:算法复杂
- 代表语言: JAVA
Golang垃圾回收机制
简单流程:
垃圾回收开始时从root对象开始扫描,把root对象引用的内存标记为”被引用”,考虑到内存块中存放的可能是指针,所以还需要递归的进行标记,全部标记完成后,只保留被标记的内存,未被标记的全部标识为未分配即完成了回收。
内存标记底部结构:
之前讲内存分配的时候,介绍过span数据结构,span中维护了一个个内存块,并由一个位图allocBits表示每个内存块的分配情况(带指针的部分)。在span数据结构中还有另一个位图gcmarkBits用于标记内存块被引用情况。
三色标记法
实际上我们将标记的,未标记的,待标记的区分开来,分别进行操作。那么我们实际操作时的逻辑,抽象一下,就是“三色标记法”。
这里的三色,对应了垃圾回收过程中对象的三种状态:
- 灰色:对象还在标记队列中等待
- 黑色:对象已被标记,gcmarkBits对应的位为1(该对象不会在本次GC中被清理)
- 白色:对象未被标记,gcmarkBits对应的位为0(该对象将会在本次GC中被清理)
最一开始,所有对象(A~F)都是白色对象
之后扫码Root对象,发现引用了AB,便将AB放入灰色对象进行分析、
发现A无引用其他对象,B还引用了D。便将AB放入黑色对象同时D放入灰色对象再分析,最后D也放入黑色对象
最后将所有白色对象回收
垃圾回收触发时机- 内存分配量达到阀值触发GC(阀值 = 上次GC内存分配量 * 内存增长率)
- 定期触发GC(默认情况下,最长2分钟触发一次GC)
- 手动触发(runtime.GC())
GC优化
GC性能与对象数量负相关,对象越多GC性能越差,对程序影响越大。
所以GC性能优化的思路之一就是减少对象分配个数,比如对象复用或使用大对象组合多个小对象等等。