Go垃圾回收和内存分配 | 青训营笔记

94 阅读2分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的的第六篇笔记

追踪垃圾回收

对象被回收的条件:指针指向关系不可达的对象

标记根对象

  • 静态变量、全局变量、常量、线程栈等

标记过程:找到可达对象

  • 求指针指向关系的传递闭包:从根对象出发,找到所有可达对象

三色标记法:

  • 初始时所有对象均在白色集合
  • 然后再遍历白色集合中的所有根对象,并把遍历到的对象加入灰色集合
  • 遍历灰色集合,将灰色集合的引用从白色集合加入灰色集合,自身加入黑色集合。
  • 重复第三步,直到灰色集合为空

引用计数

每个对象都有一个与之对应的引用数目,当且仅引用数大于0对象才能存活。

优点:

  • 内存管理的操作被平分到程序执行的过程中

缺点:

  • 维护开销大,通过原子操作保证引用计数的原子性和可见性。
  • 无法回收循环引用的数据。
  • 内存开销:每个对象都引入额外的内存空间存储引用数目

清理的方式:

  • 将存活对象复制到另一块内存空间(copying GC)
  • 将死亡对象的内存标记为 可分配,后续可以继续分配给其它对象 (Mark-sweep GC)
  • 把存活对象移动到一起(Compact GC)

分代GC

年轻代

  • 常用的对象分配
  • 由于存活对象很少,可以采用copyiny collection
  • GC吞吐率很高(GC时间/程序执行总时间)

老年代

  • 对象趋于一直存活着,反复复制开销大
  • 可以采用mark-sweep collection

Go对象分配的性能问题

内存分配:为对象在heap上分配内存

  • 系统调用mmap()向OS申请一大块内存,例如4MB。
  • 将内存划分成大块,例如8KB,称为mspan。
  • 再将大块内存分化成特定的小块,用于对象分配。
  • noscan mspan:分配不包含指针的对象——GC不需要扫描。
  • scan mspan:分配包含指针的对象——GC需要扫描。
  • 根据对象的大小,选择合适的块返回。

微信截图_20220514122015.png

  • 分配路径过长

微信截图_20220514122124.png

  • 小对象居多

解决方法

使用Balanced GC:指针碰撞风格的对象分配

每个goroutine都绑定一大块内存(1KB),称为gorountine allocation buffer,用于noscan类型 的小对象分配:<128B 维护三个指针:base,end,top。

微信截图_20220514121442.png

  • 本质,将多次对象分配合并成一次对象分配。

当GAB总大小超过阈值,将GAB中存活的对象复制到另外分配的GAB中,本质是使用了copying GC算法管理小对象