go gc

发布时间 2023-12-03 23:56:31作者: 杨阳的技术博客

垃圾回收 (Garbage Collecting)思路

1. "标记-清除〞  go的做法

2. "标记-整理〞  标记后删除, 删除后重新把内存空间整理  java 早期

3. "标记 - 复制〞  两块相似的内存, 直接把有用的拷贝到另外一块上去  java 现阶段

Go因为堆内存结构的独特优势,选择最简单的标记-清除

找到有引用的对象,剩下的就是没有引用的

如何标记

有用的对象从哪里开始找

1. 被栈上的指针引用 一块堆上对象, 现在被栈上指针引用了, 栈上是正则运行的程序 .

2. 被全局变量指针引用

3. 被寄存器中的指针引用 当做参数或者中间变量,放入了寄存器中,也是正则运行的

上述变量被称为 Root Set (GC Root)

搜索算法 有广度优先和深度优先

广度优先遍历,指的是从图的一个未遍历的节点出发,先遍历这个节点的相邻节点,再依次遍历
每个相邻节点的相邻节点。

深度优先从图中一个未访问的顶点 V 开始,沿着一条路一直走到底,然后从这条路尽头的节点回
退到上一个节点,再从另一条路开始走到底...,不断递归重复此过程,直到所有的顶点都遍历完成.

三色标记法

1. 所有的堆对象都是白色的.   
2.Root set 置为灰色   // 要开始分析 这些对象引用了 上面白色的那些对象
3. 置为黑色  // 经过分析, 被root 对象直接 或者间接引用了
4. 最终只会存在 白色 和 黑色两种, 最后一层灰色对象没有引用其他对象,置为黑色
5. 清理白色对象.

串行GC标记 STW

  Stop The World, 暂停所有其他协程
  通过可达性分析,找到无用的堆内存
  释放堆内存
  恢复所有其他协程

问题: 暂停了所有协程, 性能不好

并发标记 删除

归纳下: 当标记开始时, 如果有对象被别的对象删除了, 不引用了, 但是 又被前面已经分析过的对象引用, 就会造成, 误清理.

解决办法: gc开始后,被删除的对象,置为灰色. 就意味着不光删除对象不会被清理, 而且删除对象引用的对象也不会被清理. 如果确实没有被引用了, 下次gc时候,肯定会被删除.

下面图, 表达就是描述的信息:

删除屏障

并发标记时, 对指针释放的白色对象置灰

删除屏障可以杜绝在GC标记中被释放的指针,被清理, 真没被引用了,下次GC会被删除

并发标记 删除

类似上面的 插入 ,基本一个套路.

插入屏障

并发标记时, 对指针新指向的白色对象置为灰

混合屏障

  包含删除和插入.

  被删除的堆对象标记为灰色
  被添加的堆对象标记为灰色

GC的触发

1. 系统定时触发

  sysmon定时检查

  如果2分钟内没有过GC,触发

   p0协程一直在检查GC, 发现2分钟没有触发,就会主动触发.

在runtime的 proc.go 中有定义:

    // forcegcperiod is the maximum time in nanoseconds between garbage
    // collections. If we go this long without a garbage collection, one
    // is forced to run.
    var forcegcperiod int64 = 2 * 60 * 1e9

2. 用户显式触发

  用户调用runtime.GC方法
  并不推荐调用

3. 申请内存时触发

  给对象申请堆空间时,可能导致GC

在mallocgc 中有体现. 有兴趣的可以去看下源码.

GC优化原则

1. 尽量少在堆上产生垃圾

2. 内存池化

  类似channel的缓存空间  

3. 减少逃逸
    
    逃逸会使原本在栈上的对象进入堆中, 例如fmt包  返回指针

4. 使用空结构体

GC分析工貝

go tool pprof

go tool trace

go build -gcflags= "m"

GODEBUG =" gctrace=1"  这个最直接

GODEBUG 使用

  1. run main.go
这里主要关注 %比, 例如 5%代表 系统有5%的时间,在做GC. gc在标记阶段是并行的, 但是在一些关键节点,还是不能并行. 
这个值如果超过 10%就要排查下问题和优化了.

4 ->6->5M  GC开始时候 4M 过程中 6M 结束时候 5M

p 是线程的个数