Golang 垃圾回收
什么是根对象(roots)[1]:
根对象在垃圾回收的术语中又叫做根集合,它是垃圾回收器在标记过程时最先检查的对象,包括:全局变量:程序在编译期就能确定的那些存在于程序整个生命周期的变量。
执行栈:每个 goroutine 都包含自己的执行栈,这些执行栈上包含栈上的变量及指向分配的堆内存区块的指针。
寄存器:寄存器的值可能表示一个指针,参与计算的这些指针可能指向某些赋值器分配的堆内存区块。
常见的垃圾回收方法
引用计数
c++ 需要用户手动释放内存,容易导致内存泄露等问题,后来引入了智能指针使用的就是引用计数进行垃圾回收。对每一个对象都引入一个计数器,每引用一次计数器+1,解除引用-1,当计数器为0时即表示该对象已经不需要访问了可以回收。
优点:实现简单
不需要 STW,垃圾回收快
缺点:无法处理循环引用
需要频繁的更新计数器和递归更新计数器,可能带来性能损失
标记-清扫触发 GC:主动,定时,被动,阈值等
STW,从根对象开始扫描,标记活跃对象
清除非活跃对象
取消 STW
优点:简单
缺点:长时间的 STW
分代收集
JVM做垃圾回收时常用的GC算法
三色标记
基于标记清扫发展出来的gc方法,也是目前golang采用的gc方法,特点是:并发
增量
STW
对象颜色分类 [2]:黑色:活跃对象
白色:潜在的垃圾,gc 之前所有对象默认是白色的(根对象除外)
灰色:活跃对象,中间状态,标记结束之后只存在黑色和白色对象,每次递归的遍历灰色对象,直到灰色对象不存在
过程:gc 开始之前根对象被标记为灰色,其他均为白色,垃圾收集器从灰色对象开始遍历,并标记为黑色
将黑色对象指向的对象标记为灰色
重复1,2直到所有灰色对象遍历完成
简单的三色标记无法并发、增量执行,并且整个过程需要 STW,否则遍历过程中用户更新了指针引用,可能导致指向的内存区域被错误的回收(悬挂指针),为了并发增量的标记,减少 STW 时间,还需要引入屏障技术
屏障技术
屏障技术就是保证内存操作以一定顺序执行,确保在某一操作之前执行另一操作
在标记过程中,黑色C对象可能指到新的白色对象E,此时需要
触发写屏障,将对象 E 变为灰色
将 C 指向 E
重新扫描灰色对象 E
即(插入写屏障):
1writePointer(slot, ptr):
2 shade(ptr)
3 *slot = ptr
或:
将对象 C 指向 E
删除对象 D 指向 E 的指针
出发删除屏障将 E 变为灰色将对象 C 指向 E删除对象 D 指向 E 的指针触发删除屏障将 E 变为灰色
即(删除屏障):
1writePointer(slot, ptr)
2 shade(*slot)
3 *slot = ptr
参考资料