Loading... # 0x01 <div class="tip inlineBlock info"> 本文基于 **Go 1.14.12** 注意信息时效 </div> <div class="tip inlineBlock warning"> 本篇文章仅做 GC 过程的逻辑介绍,不包含任何算法实现(比如 Write Barrier 的过程) </div> 本文主要介绍 Go 语言中堆内存分配后开始进行 GC 发生了什么、什么是三色标记法、内存回收是什么时候进行的这几个概念。旨在于介绍 GC 的基本知识,帮助同学们了解与 GC 相关的 bug / 性能问题是如何发生的,而不是教大家如何去写 GC 的算法。 # GC 是如何触发的? Go 语言中 GC 的触发有三种方式: 1. 用户自行调用 `runtime.GC()` 触发 2. 在堆上分配内存时可能会触发 GC(我们常说的内存分配到一定量级) 3. 一段时间后(2min)自动触发 GC (我们常说的定时触发) # GC 触发后会进行哪些操作? 首先要明确一个很重要的知识点:Go 语言中的 GC 大部分过程是和用户程序 **并行** 进行的。这里引用曹大 PPT 里面的一张图,介绍整个 GC 的流程。  首先 GC 是在 GCoff 关闭状态,等到上述三种方式任意一种触发 GC 开始启动。 GC 启动后,先会进行短暂的 STW 暂停用户程序,启动 `sweep`(内存回收任务)和所有的后台标记的 worker 并且和全部的 P 绑定,然后再将他们休眠(gopark),和进行一些简单的操作。然后恢复 STW。 之后因为要控制整个 GC 的 CPU 占用在 25% 左右,分批慢慢的唤醒休眠的后台标记 worker 进行对象标记,用的三色标记法。期间,如果需要标记的对象相当多,会征用部分应用程序的 P 进行辅助标记。直到所有对象标记完毕。 最后再次进行短暂的 STW 暂停用户程序,进行一些清扫任务的工作,并唤醒 `sweep` 。然后结束标记,关闭 GC。 `sweep` 在 GC 关闭后开始后台的处理回收的内存,返还给 `mheap` ,多余的内存返还给操作系统。 # 三色标记法的过程是怎么的? 理想情况下 Go 语言 GC 的三色标记法就是标准的标记-清除法,先从 root 节点的对象(全局变量、栈上的对象等)出发进行广度优先搜索,正在标记的对象是灰色对象,标记过了的是黑色对象,没有标记到的就是白色对象。当完全完成广度优先遍历了之后,剩下的白色对象就是垃圾对象了。 <div class='album_block'> [album type="photos"]      [/album] </div> # 为啥会有 write barrier ? 我们上面提到的是理想情况下,但是 Go 的 GC 有个特别大的特点,就是 **他是和用户程序同时进行的!** 这就意味着,我们对象的引用情况可能会实时改变。而这就有可能导致对象错标、漏标,这是完全无法接受的。于是开发 Go 语言的大佬们使用混合写屏障(write barrier)的算法保证不会发生错标、漏标的情况。这里不展开算法实现了,感兴趣的同学可以看看这位大佬的讲解 [Go 语言设计与实现 - 7.2 垃圾回收器 - 屏蔽技术](https://draveness.me/golang/docs/part3-runtime/ch07-memory/golang-garbage-collector/#%E5%B1%8F%E9%9A%9C%E6%8A%80%E6%9C%AF) 。 # 因为 GC 导致的问题 网上经常能看到有人吐槽 Go 语言 GC 的性能问题,比如说没有分代而且 GC 占 CPU 高居不下。占 CPU 高居不下这个问题主要还是由于 Go 的 GC 强依赖了语言本身的 GMP 调度机制,让 GC 和用户程序并发的进行,可能 Go 的开发人员认为这样已经比其他语言 GC 过程全部都需要 STW 的语言性能好很多了。 不过既然我们已经在用 Go 进行开发,在官方没有改的情况下,只能用其他角度优化我们的程序,让 GC 更少或者更快的执行了。 ## GC 更快的执行 需要扫描的对象越少, GC 执行的越快。所以程序中尽量的少分配或者复用对象即可减少 GC 的时间,场景的操作有: - 使用 `sync.Pool` 进行堆对象重用 - 考虑使用 `slice` 替换 `map` - 考虑将指针对象替换成非指针对象 - 业务逻辑上,将多个小对象合并成一个大对象 ## GC 更少的执行 我们知道除了手动调用 GC,GC 只会定时、定量的触发,而定量一般是目前分配的堆内存翻倍的时候触发 GC。所以有个邪道的方法就是在内存相当富足的情况下,在程序开始时分配一个超大的对象,占着 XX 不 XX。然后分配比他小的对象时,就不那么容易触发定量的 GC 了。 # Reference [Go 语言设计与实现 - 7.2 垃圾收集器](https://draveness.me/golang-garbage-collector/) [图解Go语言内存分配](https://qcrao.com/2019/03/13/graphic-go-memory-allocation/) 最后修改:2021 年 08 月 19 日 10 : 52 PM © 允许规范转载 赞赏 如果觉得我的文章对你有用,请随意赞赏 赞赏作者 支付宝微信