golang 的垃圾回收机制如何?与 Python 和 Java 的垃圾回收有何异同?

Java 语言是基于分代的,在新生代主要使用了标记-复制,在新生代中分为 eden,s0 区和 s1 区。
其中,绝大多数对象都是直接在 eden 区进行分配,然后 s0 区和 s1 区空闲的区域进行复制工作。而老年代主要使用标记-整理算法,将标记后的对象统一移动到内存区域的前部分,然后对后面的区域进行清理。
而 go 当中主要使用了标记-清除算法,其主要原理是对对象进行标记,然后回收未被标记过的对象。具体的算法就不在此详述,本文想要探索的是 java 和 go 当中垃圾回收的不同点。

要想找到不同点,首先要找到相同点。即标记,三种算法均是基于标记算法产生的,不同地方在于对于标记后的行为的处理,但这其实并不是关键,因为不管是复制、整理还是清除,均是基于内存区域特点或语言特点决定的,如新生代常常大批量死亡,所以只需要复制存活对象,这样效率高,而老年代主要是大对象以及存活时间较长的对象,变化不大,因此进行整理,而 go 语言追求简单,因此使用清除。

三色标记法垃圾回收

一开始所有的引用都是白色。
从栈和全局变量出发进行遍历,先把栈和全局变量标为灰色,标灰是入队的过程。
对于队列中的结点,遍历它所指向的引用,把它的邻居放进队列。
当队列中节点出队时,把结点标记为黑色,表示已经处理结束。
遍历完成之后,最终为白色的就是垃圾。

应该让白色垃圾尽量少,因为白色垃圾一旦错误回收,就会产生致命错误;而白色垃圾延迟回收,在下一轮垃圾回收中依旧能够回收掉。
如果是灰色结点引用了白色结点,那不需要做任何处理,只需要等处理灰色结点的时候就能把这个白色结点染成灰色。
如果是黑色结点引用了白色结点,这种需要特殊处理,不然这个白色结点明明在使用,结果被回收了。
Java 的 CMS 垃圾回收器就是写屏障+增量更新模式。当黑色结点引用白色结点的时候,把白色结点放入队列。

垃圾回收中的写屏障 write barrier 是什么?

写屏障是一种编译技术,可以用于优化垃圾回收。
把 Java、Go 编译的时候,涉及到引用变化的语句,插入一些写屏障。其实就是跟垃圾回收器做一些通信。