每日三日一GO -- GO如何标记内存

原文地址

https://translate.google.cn/?sl=en&tl=zh-CN&text=The Go garbage collector is responsible for collecting the memory that is not in use anymore. The implemented algorithm is a concurrent tri-color mark and sweep collector. In this article%2C we will see in detail the marking phase%2C along with the usage of the different colors.&op=translate

正文

Go 垃圾回收器负责收集不再使用的内存。 实现的算法是并发三色标记和扫描收集器。 在本文中,我们将详细了解标记阶段以及不同颜色的使用。

您可以在 Ken Fox 的“visualizing Garbage Collection Algorithms”中找到有关不同类型垃圾收集器的更多信息。

阅读这篇文章之前请先了解GO内存的管理和分配

标记阶段

此阶段执行内存扫描以了解我们的代码仍在使用哪些内存(block)以及应回收哪些内存(block)。

但是,由于垃圾回收与我们的 Go 程序同时运行,因此它需要一种在扫描时检测内存中潜在变化的方法。 为了解决这个问题,实现了一种写屏障算法,并允许 Go 跟踪任何指针变化。 启用写屏障的唯一条件是短时间停止程序,也称为“Stop the World”:
在这里插入图片描述

Go 还在进程开始时为每个处理器启动一个标记工作者,帮助标记内存。然后,一旦roots(内存结构的根节点)被排队进行处理,标记阶段就可以开始遍历和着色内存。

现在让我们举一个简单程序的例子,它可以让我们跟踪标记阶段完成的步骤:

type struct1 struct {
	a, b int64
	c, d float64
	e *struct2
}

type struct2 struct {
	f, g int64
	h, i float64
}

func main() {
	s1 := allocStruct1()
	s2 := allocStruct2()

	func () {
		_ = allocStruct2()
	}()

	runtime.GC()

	fmt.Printf("s1 = %X, s2 = %X\n", &s1, &s2)
}

//go:noinline
func allocStruct1() *struct1 {
	return &struct1{
		e: allocStruct2(),
	}
}

//go:noinline
func allocStruct2() *struct2 {
	return &struct2{}
}

由于 struct subStruct 不包含任何指针,因此它存储在专用于对象的span中,不引用其他对象:

在这里插入图片描述

这使得垃圾收集器的工作更容易,因为它在标记内存时不必扫描这个span(span区分了需要scan 和 no scan)。

一旦分配完成,我们的程序就会强制垃圾回收器运行一个周期。 这是工作流程:

在这里插入图片描述

垃圾回收器从栈开始,并递归地跟随指针遍历内存。 标记为 no scan 的 Span 会停止扫描。 但是,这个过程并不是由同一个 goroutine 完成的; 每个指针都在会加入一个工作池中排队。 然后,后台的标色工作者(前文提到的)将会从这个池子将对象取出,并扫描对象,将对象中指针指向的对象重新放入work pool 中。

在这里插入图片描述

标色

现在需要一种方法来跟踪哪些内存已被扫描。 垃圾回收器使用三色算法,其工作方式如下:

  • 所有对象一开始都被认为是白色的
  • 根对象(堆栈、堆、全局变量)将显示为灰色

完成此主要步骤后,垃圾回收器将:

  • 将灰色对象标记为黑色
  • 跟随来自该对象的所有指针并将所有引用的对象着色为灰色

然后,它会重复这两个步骤,直到没有更多的对象要着色。 从这一点来看,对象要么是黑色的,要么是白色的。 白色集合代表未被任何其他对象引用并且准备好被回收的对象。

过程如下图:

在这里插入图片描述

第一步,所有对象都被认为是白色的。 然后,遍历对象,可到达的对象将变为灰色。 如果对象在标记为无扫描的范围内,则可以将其涂成黑色,因为它不需要扫描:

在这里插入图片描述

灰色对象现在排队等待扫描并变为黑色:

在这里插入图片描述

对象入队也会发生同样的事情,直到没有更多的对象要处理:

在这里插入图片描述

在进程结束时,当白色对象是要收集的对象时,黑色对象是内存中正在使用的对象。 正如我们所见,由于 struct2 的实例是在匿名函数中创建的,并且无法从堆栈中访问,因此它保持白色并且可以清除。

由于每个span中名为 gcmarkBits 的位图属性,颜色在内部实现,该属性通过将相应位设置为 1 来跟踪扫描:

在这里插入图片描述

正如我们所见,黑色和灰色的工作方式相同。 该过程的不同之处在于,当黑色对象结束链时,灰色将要扫描的对象排入队列。

垃圾回收进行时停止了世界,将在每个写屏障上所做的更改刷新到工作池并执行剩余的标记。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值