Skip to content

Latest commit

 

History

History
151 lines (83 loc) · 4.3 KB

[Go] GC.md

File metadata and controls

151 lines (83 loc) · 4.3 KB

垃圾回收算法

https://draveness.me/golang/docs/part3-runtime/ch07-memory/golang-garbage-collector/

有两种常见的自动管理堆内存的方法:

  • 引用计数/智能指针
  • 追踪式垃圾回收(对堆内存的对象关系图进行可达性分析)

术语

  • 根对象: 包括所有栈上对象,全局变量

标记-清扫法(mark-sweep)

典型的STW(stop the world)算法,当进行垃圾回收时,先暂停用户程序,然后从根对象出发对堆对象进行可达性标记(比如bfs/dfs),标记完后遍历所有的堆对象,回收掉不可达对象

三色标记法

这是一种并发算法,不需要或只需要短暂的暂停用户程序

定义三色: 黑白灰

  • 灰色: 可达对象
  • 黑色: 由灰色衍生出来的可达对象
  • 白色: 不可达对象

算法流程:

首先所有根对象置为灰色

  1. 从灰色对象中选择一个,置为黑色对象
  2. 将黑色对象指向的所有对象置为灰色
  3. 重复1,2,直到不存在灰色对象
  4. 回收剩余的白色对象

由于在标记过程中,对象图可能改变,所以需要作如下操作:

  • 修改指针之前,必须先对被指向的对象标记为灰色,由于现代cpu的乱序执行和多发射,这需要我们用写内存屏障来实现

屏障

  • 插入写屏障

    • writePointer(slot, ptr):
          shade(ptr)  //染灰
          *slot = ptr
  • 删除写屏障

    • writePointer(slot, ptr)
          shade(*slot) //染灰
          *slot = ptr
  • 混合写屏障(v1.8引入)

    • writePointer(slot, ptr):
          shade(*slot)
          if current stack is grey:
              shade(ptr)
          *slot = ptr

注意: 不会在所有的根对象上开启写屏障,因为一个程序可能由成百上千个goroutine,如果在所有的goroutine的栈上开启写屏障,压力太大


看了了b站刘丹冰的视频后,有了更深的理解

go的gc算法:

v1.3 StopTheWorld 标记清扫法 stw->mark->sweep->stw

优化: stw->mark->stw->sweep(串行标记,并发清扫)

v1.5 三色标记法 这里的mark是分层的bfs.

这个方法可以解决并发吗?

对于新创建的对象:

对于已有对象的并发标记问题:

如果一个白色对象原来被正常引用(或是作为白色新创建节点,被黑色对象引用), 在mark过程中变为仅被黑色对象引用,显然就会最终仍是白色,被丢失,这是因为三色标记法是基于灰色对象标记下一个灰色对象的,不会对黑色对象所引用的对象标记

解决方法:

强三色不变性: 禁止mark过程中的黑色对象指向白色对象

弱三色不变性: 一个白色对象的上游链路必须存在灰色对象,此时其可被黑色对象引用(只要上游灰色存在,最终一定会标记到自己)

如何实现:

屏障技术(实际上,就是在变更引用关系的时候触发回调函数)

初始时 A.field1 = B

变更: A.field1 = C

则: B触发删除写屏障,C触发插入写屏障(要么开启删除写屏障,要么开启插入写屏障)

最后v1.8 优化为混合写屏障

插入写屏障:

​ 被黑色节点引用的对象置灰色

​ 1. 堆空间作为根节点时,白色对象若被黑色对象引用,触发回调,自己置为灰色

​ 2. 栈空间不触发插入屏障,这是为了保证速度. 所以为了保证新节点不丢失,要最后stw扫描栈,重新扫描mark一次

因此:

插入写屏障的不足: 结束时stw扫描栈,10-100ms

删除写屏障:

​ 被删除的对象直接置为灰色

​ 显然,如果这个对象确实是不再被引用,而不是变更为被黑色对象引用,那么这个对象就会在本轮不被删除,但是无论如何,下轮gc仍然删除.所以缺点就是有可能造成延迟删除.但是这不可避免,因为你无法判断他是变更引用关系到黑色对象上,还是真的删了,只能先妥协一轮.

v1.8混合写屏障:

  1. 栈上对象(根节点为栈)全部置为黑色,后续被栈引用的新对象均置为黑色(防止重复stw扫描栈)
    1. 栈不开启写屏障
  2. 堆上被删除的对象置为灰色
  3. 堆上被插入的对象置为灰色

满足弱三色不变性

注意这里,栈对象也是分配在堆上的,因为go程是用户态的,详见GMP