请简单描述 Go 语言 GC(垃圾回收)的工作原理

请尝试在评论区里写下答案(如不能清楚表述,那么你可能没真正理解)。欢迎参与,为下一次求职做准备。

请在评论区留下你的答案

摈弃世俗浮躁,追求技术精湛
本帖已被设为精华帖!
本帖由系统于 2年前 自动加精
Summer
讨论数量: 5

之前做过一些简短的总结

三色标记,黑白灰三色,初始都是白色

  • 白色,对象未被标记
  • 黑色,对象已被标记,且子对象均被标记
  • 灰色,对象已被标记,但对象包含的子对象未标记

根据顺序,先进行根对象分析,将白色对象转为灰色,然后进行灰色分析,如果不存在引用子对象(白色),转为黑色,如果存在引用子对象,那么引用子对象变成灰色,被分析的灰色对象变为黑色,再继续灰色分析,直到不存在灰色,就将白色删除,黑色保留。

触发条件:

  • 默认是内存扩大一倍
  • 2min 定时触发
  • 手动执行 runtime.gc()

GC 过程会通过写屏障实现并发,但在标记开始和结束时还是会有 STW

2年前 评论

我也说下我梳理的

Go的GC用了三色标记法、屏障技术、增量与并发实现的。

三色标记法

三色标记法的颜色

三色标记法分白色、灰色、黑色
● 白色 —– 潜在的垃圾(注意是潜在)
● 灰色 —- 活跃的对象
● 黑色 —- 活跃的对象

三色标记法的过程

三色标记法的过程是:

  • 从灰色队列里取出灰色对象,并把它标记成黑色。
  • 把黑色对象所引入的外部对象标记成灰色(本来就是灰的保持原状)。
  • 重复上述操作直至灰色队列为空,标记结束。

屏障技术

屏障技术是为了保证屏障前的操作优于屏障后的操作。主要是保证顺序性的。

屏障技术相当于是钩子函数,它是在用户程序读取对象、更新对象、创建对象指针时执行的一段代码,故又分为读屏障和写屏障。由于在读操作远远高于写操作所以为了性能,都是以写屏障来保证三色不变性。
Go在1.8版本的时候结合了写屏障和删除写屏障。该写屏障会将被覆盖的对象标记成灰色并在当前栈没有扫描时将新对象也标记成灰色。

注意 混合屏障技术是在go 1.8版本才引入的,就是结合了写屏障和删除写屏障,目的是将被覆盖的对象标记成灰色并在当前栈没有扫描时将新对象也标记成灰色。

增量与并发

此技术的出现为了解决暂停时间过长的手段。由于垃圾收集器在执行阶段会暂停应用程序,但是很多实时的应用程序是无法接受长时间的暂停的,这两个技术也就应运而生。

增量收集器

增量收集器为了降低程序最长暂停时间的一种方案,这个方案也是非常直接,直接把这个较长的暂停时间,切分成多个小的GC时间片。在整体来看增加了总的暂停时间,但是确实是降低了最长暂停时间。

并发收集器

并发收集器不仅可以降低最长暂停时间而且还可以降低垃圾收集阶段的暂停时间。

最长暂停时间是指,垃圾收集器一个任务执行的最长时间
总的暂停时间是指,垃圾收集器从开始暂停到结束的时间。

通过开启屏障、利用多核优势与用户程序并行执行。由于是要开启屏障,又与用户程序一起执行,所以会有额外开销,而且会增加总的暂停时间,还会影响用户程序。

Go的版本之间差异是有的所以面试的时候一定要注意版本。

2年前 评论

之前做过一些简短的总结

三色标记,黑白灰三色,初始都是白色

  • 白色,对象未被标记
  • 黑色,对象已被标记,且子对象均被标记
  • 灰色,对象已被标记,但对象包含的子对象未标记

根据顺序,先进行根对象分析,将白色对象转为灰色,然后进行灰色分析,如果不存在引用子对象(白色),转为黑色,如果存在引用子对象,那么引用子对象变成灰色,被分析的灰色对象变为黑色,再继续灰色分析,直到不存在灰色,就将白色删除,黑色保留。

触发条件:

  • 默认是内存扩大一倍
  • 2min 定时触发
  • 手动执行 runtime.gc()

GC 过程会通过写屏障实现并发,但在标记开始和结束时还是会有 STW

2年前 评论

我也说下我梳理的

Go的GC用了三色标记法、屏障技术、增量与并发实现的。

三色标记法

三色标记法的颜色

三色标记法分白色、灰色、黑色
● 白色 —– 潜在的垃圾(注意是潜在)
● 灰色 —- 活跃的对象
● 黑色 —- 活跃的对象

三色标记法的过程

三色标记法的过程是:

  • 从灰色队列里取出灰色对象,并把它标记成黑色。
  • 把黑色对象所引入的外部对象标记成灰色(本来就是灰的保持原状)。
  • 重复上述操作直至灰色队列为空,标记结束。

屏障技术

屏障技术是为了保证屏障前的操作优于屏障后的操作。主要是保证顺序性的。

屏障技术相当于是钩子函数,它是在用户程序读取对象、更新对象、创建对象指针时执行的一段代码,故又分为读屏障和写屏障。由于在读操作远远高于写操作所以为了性能,都是以写屏障来保证三色不变性。
Go在1.8版本的时候结合了写屏障和删除写屏障。该写屏障会将被覆盖的对象标记成灰色并在当前栈没有扫描时将新对象也标记成灰色。

注意 混合屏障技术是在go 1.8版本才引入的,就是结合了写屏障和删除写屏障,目的是将被覆盖的对象标记成灰色并在当前栈没有扫描时将新对象也标记成灰色。

增量与并发

此技术的出现为了解决暂停时间过长的手段。由于垃圾收集器在执行阶段会暂停应用程序,但是很多实时的应用程序是无法接受长时间的暂停的,这两个技术也就应运而生。

增量收集器

增量收集器为了降低程序最长暂停时间的一种方案,这个方案也是非常直接,直接把这个较长的暂停时间,切分成多个小的GC时间片。在整体来看增加了总的暂停时间,但是确实是降低了最长暂停时间。

并发收集器

并发收集器不仅可以降低最长暂停时间而且还可以降低垃圾收集阶段的暂停时间。

最长暂停时间是指,垃圾收集器一个任务执行的最长时间
总的暂停时间是指,垃圾收集器从开始暂停到结束的时间。

通过开启屏障、利用多核优势与用户程序并行执行。由于是要开启屏障,又与用户程序一起执行,所以会有额外开销,而且会增加总的暂停时间,还会影响用户程序。

Go的版本之间差异是有的所以面试的时候一定要注意版本。

2年前 评论
playmaker

黑白灰 标记 回收白色垃圾

2年前 评论

标记清理(Mark And Clean)

用户态代码并发执行(会在清理结束阶段和标记结束阶段STW)

三色标记法

弱三色和写屏障

2年前 评论

我觉得在回答这个面试题的时候,最关键的点应该是说到 “三色标记法” “混合写屏障”

三句话总结

  1. go语言的垃圾回收机制最初(1.3版本前)采用的是普通标记清除的方法,整个垃圾回收过程需要启动STW,效率很低;
  2. 后来(1.5版本)采用并发标记清除算法,具体实现是:三色标记法 + 插入写屏障/删除写屏障,通过插入写屏障和删除写屏障机制来减少STW的时间,这种做法对垃圾回收效率有所提高,但是需要将栈上元素全部变成白色,再重新扫描一遍栈,这个时候还是需要启动STW,所以效率只能说是普通;
  3. 现在(1.8版本以后)采用并发标记清除算法,具体实现是:三色标记法 + 混合写屏障,在整个垃圾回收的过程中,不需要启动STW,效率很高。

    问:为什么1.5版本的三色标记法要重新扫描一遍栈呢?
    答:因为在扫描的过程中,用户代码和标记清除算法并发执行,而插入写屏障/删除写屏障 只在堆上开启,栈上没有开启写屏障,所以栈上对象的引用可能会受到并发执行的影响,比如说:突然插入了一个可达的新对象,但是此时所有可达对象都已变成黑色,那这个新对象有可能就没有办法标记到,因此需要将栈上的元素标记为白色,再从头开始重新扫描,重新标记一遍。

    问:为什么栈上不开启写屏障?
    答:goroutine在并发的时候,大多数操作都在栈上,如果对栈开启写屏障保护的话,那性能会有严重影响


三色标记法具体过程

  1. 程序刚开始时,所有对象默认标记为白色
  2. 从根节点开始遍历,所有可达对象白色标记为灰色,并将灰色对象放到灰色标记表中
  3. 遍历灰色标记表,将灰色对象标记为黑色,并将其由灰色标记表移动到黑色标记表中


混合写屏障是怎么工作的

  1. 在gc开始的时候,将栈上所有的可达对象都标记为黑色,在栈上新创建的对象都标记为黑色
  2. 堆上添加的对象标记为灰色删除指向该对象的所有指针,那么这个对象就会变成灰色


参考文章

刘丹冰大佬关于go语言gc的讲解:博客:[Golang三关-典藏版] Golang三色标记混合写屏障GC模式全分析

10个月前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!