垃圾回收 (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. 用户显式触发
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 使用
  - 这里主要关注 %比, 例如 5%代表 系统有5%的时间,在做GC. gc在标记阶段是并行的, 但是在一些关键节点,还是不能并行.
- 这个值如果超过 10%就要排查下问题和优化了.
- 4 ->6->5M GC开始时候 4M 过程中 6M 结束时候 5M
- p 是线程的个数
复制代码 免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |