一分钟系列:go语言运行时-垃圾回收

在介绍垃圾回收之前,我们得先知道垃圾回收的作用是什么?

垃圾回收能让让我们省去自己释放内存的烦恼,go语言的运行时会帮我们自动去释放不用了的内存。

在介绍go垃圾回收原理之前,我们先了解下常见的垃圾回收方法。

引用计数

引用计数是对我们使用的每块内存都做一个计数器,当计数器为0的时候,就释放这块内存。

优点

  • 简单清晰,效率高,因为每个对象都有自己的计数器。

缺点

  • 容易内存泄漏:不好维护计数器
  • 不支持循环引用

代龄

根据对象存活时间,分为不同的组别。

优点

  • 有效减少每次gc时扫描的对象:每次都将存活时间长的对象放入老生代
  • 内存压缩:发生代龄移动的时候,可以做内存的压缩,减少碎片,提高利用率

标记清理

go语言使用的是3色标记,释义如下:

一分钟系列:go语言运行时-垃圾回收

1. 有黑白灰三个集合. 初始时所有对象都是白色

2. 从Root对象开始标记, 将所有可达对象标记为灰色

3. 从灰色对象集合取出对象, 将其引用的对象标记为 灰色, 放入灰色集合, 并将自己标记为黑色

4. 重复第三步, 直到灰色集合为空, 即所有可达对象都 被标记

5. 标记结束后, 不可达的白色对象即为垃圾. 对内存进 行迭代清扫, 回收白色对象.

6. 重置GC状态


当我们进行标记的时候,如果程序还在不断的申请、释放指针,会导致已经扫描的数据不正确的,这个时候粗暴的做法就是STW(stop the world)?

怎么解决?如何介绍stw的时间?

将垃圾回收的过程分为:扫描和清理两个阶段。

如果我们扫描阶段是stw,那清理阶段我们是可以和用户逻辑是并行的,因为这个时候,白色的对象已经确认不会被引用了。

所以清理阶段是可以并行的,那标记阶段,如果不stw可能带来的问题是:

一分钟系列:go语言运行时-垃圾回收

可以看到,此时当开始扫描的时候,A被标记为黑色,然后在A标记为黑色,B还么扫描之前,将A指向了C,此时扫描B的时候就无法找到C了,导致C被误回收,那这个怎么解决呢?

我们希望在stw期间,如果已经标记为黑色的对象发生了引用的改变,我们将其重新标记为灰色,然后进行扫描,就能有效解决问题,这个技术在go中就是“写屏障”。

总结

以上即是go运行时垃圾收回的全部,总结下整篇短文。

垃圾回收常见的算法有:引用计数、代龄、标记清理,go主要使用的标记清理,并在此基础上对标记、清理都进行了并发,下面引用一张Go 1.12 Release的gc图:

一分钟系列:go语言运行时-垃圾回收

参考

https://reading.developerlearning.cn/reading/64-2019-10-24-go-runtime/


分享到:


相關文章: