Go Gc
GC 版本迭代 v1.1 STW v1.3 Mark STW v1.5 三色标记 v1.8 混合写屏障 三色标记 不变性条件,写屏障 如何标记,gcmarkerbits,0表示白色对象,1表示灰色或黑色对象。p中wbBuf和gcw以及全局workbuf来表示标记队列 对象元信息,查找到对象分配内存的span gc触发时机 分配内存时,当前已分配内存与上次内存比较 sysmon每2min检查执行一次 调用runtime.gc强制执行 三色标记 传统的标记-清除需要长时间STW以完成标记和清扫的过程,三色标记用于改进减小STW的时间。 三色标记中将对象分为三种类型: 白色:可能存活的对象,在初始阶段所有对象为白色,在标记完成后,所有的白色对象视为垃圾 灰色:确认存活的对象,但其引用了白色对象,因此要对灰色对象进行递归扫描 黑色:确认存活的对象,扫描完成的对象,根对象可达 三色标记过程: 初始时所有对象为白色 将根对象标记为灰色,根对象包括运行栈中的对象以及全局对象 对所有灰色对象进行扫描,将灰色对象引用的对象标记为灰色,并将该灰色对象标记为黑色 重复上述过程,直到没有灰色对象 标记结束后,程序中只有黑色对象和白色对象,黑色对象为确认存活的对象,白色对象为垃圾 三色不变性 当三色标记的标记过程是STW时,可以确保标记过程的正确性,但STW要消耗大量时间。但如果将三色标记的标记过程和用户代码并发执行,则可能出现对象丢失. 三色标记正确性被破坏,如下图,B对象本不该回收的对象,由于引用的改变,导致其被回收了。 图来自https://golang.design/under-the-hood/zh-cn/part2runtime/ch08gc/barrier/ 使得三色标记正确性被破坏的两个条件: 黑色对象引用了白色对象 灰色对象达到白色对象的未访问过的路径被破坏,即黑色对象引用的白色对象,但灰色对象无法找到一条路径到达该白色对象 当上述两个条件同时满足时,三色标记就可能丢失对象。 想要使得三色标记正确,就必须破坏上述条件中的任意一个条件,因此三色标记不变式: 强三色不变式:黑色对象不能指向白色对象,只能指向灰色或黑色对象。该不变式破坏了两个条件。 弱三色不变式:黑色对象执行的白色对象,必须存在一条从灰色对象经过零个或多个白色对象可达该白色对象的路径。 屏障 Dijkstra插入写屏障 当某一对象的引用被插入到已经标记为黑色的对象中,需要将其标记为灰色对象。 将有存活可能的对象标记为灰色,以满足强三色不变式。 // Dijkstra 插入屏障 // slot表示旧指向的对象,ptr表示新指向的对象 func DijkstraWritePointer(slot *unsafe.Pointer, ptr unsafe.Pointer) { // 将新指向的对象标记为灰色 shade(ptr) *slot = ptr } 特点: 可能产生部分黑色对象的垃圾,需要在下一次GC中回收 对于栈上的对象,使用插入写屏障后耗费大量性能,因此不在栈上开启插入写屏障。 由于在栈上不开启插入写屏障,因此当栈上的黑色对象指向了白色的对象时,因为没有屏障,因此白色对象会被错误回收。因此插入写屏障在标记结束后,会STW并再次重新扫描栈。 Yuasa删除写屏障 起始时,STW将整个栈的可达对象标记为黑色,将所有可达对象在灰色保护下...