1、sysmon 有什么作用
sysmon 也叫 监控线程 ,变动的周期性检查,好处:
- 释放闲置超过 5 分钟的 span 物理内存 ;
- 如果超过 2 分钟没有垃圾回收, 强制执行 ;
- 将长时间未处理的 netpoll 添加到全局队列;
- 向长时间运行的 G 任务 发出抢占调度 (超过 10ms 的 g,会进行 retake);
- 收回因 syscall 长时间阻塞的 P;
2、三色标记原理
我们首先看一张图,大概就会对 三色标记法 有一个大致的了解:
原理 :
首先把所有的对象都放到白色的集合中
- 从 根节点 开始遍历对象,遍历到的白色对象从白色集合放到灰色集合中;
- 遍历灰色集合中的对象,把灰色对象引用的 白色集合的对象 放入到灰色集合中,同时把遍历过的灰色集合中的对象放到黑色的集合中;
- 循环步骤 3 ,直到灰色集合中没有对象;
- 步骤 4 结束后,白色集合中的对象就是 不可达对象 ,也就是垃圾,进行回收;
3、插入写屏障
golang 的回收没有混合屏障之前,一直是 插入写屏障 ,由于栈赋值没有 hook 的原因,所以栈中没有启用写屏障,所以有 STW 。
golang 的解决方法是:只是需要在结束时启动 STW 来重新扫描栈。这个自然就会导致整个进程的赋值器卡顿,
所以后面 golang 是引用 混合写屏障 解决这个问题。 混合写屏障之后,就没有 STW 。
4、删除写屏障
golang 没有这一步,golang 的内存写屏障是由 插入写屏障 到 混合写屏障 过渡的。
简单介绍一下, 一个对象即使被删除了最后一个指向它的指针也依旧可以活过这一轮,在下一轮 GC 中被清理掉 。
5、写屏障
Go 在进行三色标记的时候并没有 STW,也就是说,此时的对象还是可以进行修改。那么我们考虑一下,下面的情况:
我们在进行 三色标记 中扫描灰色集合中,扫描到了对象 A,并标记了对象 A 的所有引用,这时候,开始扫描对象 D 的引用,
而此时,另一个 goroutine 修改了 D->E 的引用,变成了如下图所示:
这样会不会导致 E 对象就扫描不到了,而被误认为白色对象,也就是垃圾, 写屏障就是为了解决这样的问题。
引入写屏障后,在上述步骤后,E 会被认为是存活的,即使后面 E 被 A 对象抛弃,E 会被在下一轮的 GC 中进行回收,这一轮 GC 中是不会对对象 E 进行回收的。
6、混合写屏障
- 混合写屏障 继承 了插入写屏障的优点,起始无需 STW 打快照, 直接并发扫描垃圾即可 ;
- 混合写屏障 继承 了删除写优点,赋值器是黑色赋值器, GC 期间,任何在栈上创建的新对象,均为黑色 。扫描过一次就不需要扫描了,这样就消除了插入写屏障时期最后 STW 的重新扫描栈;
- 混合写屏障扫描精度 继承 了删除写屏障,比插入写屏障更低,随着带来的是 GC 过程全程无 STW;
- 混合写屏障 扫描栈虽然没有 STW,但是扫描某一个具体的栈的时候,还是要停止这个 goroutine 赋值器的工作的(针对一个 goroutine 栈来说,是暂停扫的,要么全灰,要么全黑哈, 原子状态切换 )。
7、GC 触发时机
主动触发 :调用 runtime.GC
被动触发 :
使用系统监控,该触发条件由 runtime.forcegcperiod 变量控制, 默认为 2 分钟 。当超过 2 分钟没有产生任何 GC 时, 强制触发 GC 。
使用 步调(Pacing)算法 ,其核心思想是 控制内存比例 。如 Go 的 GC 是一种 比例 GC ,下一次 GC 结束时的堆大小和上一次 GC 存活堆大小成比例。
由 GOGC 控制,默认 100,即 2 倍的关系,200 就是 3 倍。
当 Go 新创建的对象所占用的内存大小,除以上次 GC 结束后 保留下来的对象 占用内存大小。
8、Go 语言中 GC 的流程是什么
当前版本的 Go 以 STW 为界限,可以将 GC 划分为 五个阶段 :
阶段 | 说明 | 赋值器状态 |
GCMark | 标记准备阶段 ,为并发标记做准备工作,启动写屏障 | STW |
STWGCMark | 扫描标记阶段 ,与赋值器并发执行,写屏障开启并发 | 并发 |
GCMarkTermination | 标记终止阶段 ,保证一个周期内标记任务完成 | STW |
STWGCoff | 内存清扫阶段 ,将需要回收的内存还到堆中,写屏障关闭 | 并发 |
GCoff | 内存归还阶段 ,将过多的内存还给操作系统,写屏障关闭 | 并发 |
9、GC 如何调优
通过 go tool pprof 和 go tool trace 等工具
- 控制内存分配的速度 , 限制 goroutine 的数量 ,从而提高赋值器对 CPU 的利用率。
- 减少并复用内存 ,例如使用 sync.Pool 来复用需要频繁创建临时对象,例如提前分配足够的内存来降低多余的拷贝。
- 需要时, 增大 GOGC 的值,降低 GC 的运行频率 。
更多 golang 干货,请关注我