虽然Golang的GC自打一开始,就被人所诟病,可是通过这么多年的发展,Golang的GC已经改善了很是多,变得很是优秀了。golang
如下是Golang GC算法的里程碑:算法
经典的GC算法有三种:引用计数(reference counting)
、标记-清扫(mark & sweep)
、复制收集(Copy and Collection)
。安全
Golang的GC算法主要是基于标记-清扫(mark and sweep)
算法,并在此基础上作了改进。所以,在此主要介绍一下标记-清扫(mark and sweep)算法,关于引用计数(reference counting)和复制收集(copy and collection)可自行百度。微信
此算法主要有两个主要的步骤:markdown
第一步,找出可达的对象,而后作上标记。 第二步,回收未标记的对象。并发
操做很是简单,可是有一点须要额外注意:mark and sweep算法在执行的时候,须要程序暂停!即stop the world
。 也就是说,这段时间程序会卡在哪儿。故中文翻译成卡顿
。oop
咱们来看一下图解:spa
开始标记,程序暂停。程序和对象的此时关系是这样的:线程
而后开始标记,process找出它全部可达的对象,并作上标记。以下图所示:翻译
标记完了以后,而后开始清除未标记的对象:
而后垃圾清除了,变成了下图这样。
最后,中止暂停,让程序继续跑。而后循环重复这个过程,直到process生命周期结束。
标记-清扫(Mark And Sweep)算法这种算法虽然很是的简单,可是还存在一些问题:
这里面最重要的问题就是:mark-and-sweep 算法会暂停整个程序。
Go是如何面对并这个问题的呢?
咱们先来看看Golang的三色标记法的大致流程。
首先:程序建立的对象都标记为白色。
gc开始:扫描全部可到达的对象,标记为灰色
从灰色对象中找到其引用对象标记为灰色,把灰色对象自己标记为黑色
监视对象中的内存修改,并持续上一步的操做,直到灰色标记的对象不存在
此时,gc回收白色对象。
最后,将全部黑色对象变为白色,并重复以上全部过程。
好了,大致的流程就是这样的,让咱们回到刚才的问题:Go是如何解决标记-清除(mark and sweep)算法中的卡顿(stw,stop the world)问题的呢?
标记-清除(mark and sweep)算法的STW(stop the world)操做,就是runtime把全部的线程所有冻结掉,全部的线程所有冻结意味着用户逻辑是暂停的。这样全部的对象都不会被修改了,这时候去扫描是绝对安全的。
Go如何减短这个过程呢?标记-清除(mark and sweep)算法包含两部分逻辑:标记和清除。 咱们知道Golang三色标记法中最后只剩下的黑白两种对象,黑色对象是程序恢复后接着使用的对象,若是不碰触黑色对象,只清除白色的对象,确定不会影响程序逻辑。因此:清除操做和用户逻辑能够并发。
标记操做和用户逻辑也是并发的,用户逻辑会时常生成对象或者改变对象的引用,那么标记和用户逻辑如何并发呢?
咱们看以下图,在此状态下:process程序又新生成了一个对象,咱们设想会变成这样:
可是这样显然是不对的,由于按照三色标记法的步骤,这样新生成的对象A最后会被清除掉,这样会影响程序逻辑。
Golang为了解决这个问题,引入了写屏障
这个机制。 写屏障:该屏障以前的写操做和以后的写操做相比,先被系统其它组件感知。 通俗的讲:就是在gc跑的过程当中,能够监控对象的内存修改,并对对象进行从新标记。(实际上也是超短暂的stw,而后对对象进行标记)
在上述状况中,新生成的对象,一概都标位灰色!
即下图:
看以下图,一个黑色对象引用了曾经标记的白色对象。
这时候,写屏障机制被触发,向GC发送信号,GC从新扫描对象并标位灰色。
所以,gc一旦开始,不管是建立对象仍是对象的引用改变,都会先变为灰色。
参考文献:
互联网技术窝
或者加微信共同探讨交流: