「高性能 Go 语言发行版优化与落地实践」| 青训营笔记

107 阅读3分钟

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

性能优化

  • 业务层优化
  • 语言运行时优化 解决更通用的问题
  • 数据驱动 利用pprof

自动内存管理(垃圾回收)

相关概念

  • 动态内存:程序在运行时根据需求动态分配的内存
  • 自动内存管理:由程序语言的运行时系统管理动态内存,解决double-free,use-after-free等等问题
  • 三个任务:为新对象分配空间,找到存活对象,回收死亡对象的内存空间。
  • Mutator: 业务线程,分配新对象,修改对象指向关系。
  • Collector: GC线程,找到存活对象,回收死亡对象的内存空间。
  • Serial GC: GC算法中只有一个collector.(会暂停,即stop the world)
  • Parallel GC: 支持多个collector同时回收的GC算法。(会暂停)
  • Concurrent GC: mutator(s)和collector(s)可以同时执行。collector需要感知到对象指向关系的变化,当已标记的对象指向未标记的对象时需要把未标记的对象标记。
  • 吞吐率:1-GC时间/程序执行总时间

追踪垃圾回收

1. 标记根对象 (静态变量,全局变量,常量,线程栈等)

2. 标记:找到可达对象 从根对象出发,找到所有可达对象

3. 清理: 所有不可达对象

  • Coping GC: 将存活对象复制到另外的内存空间
  • Mark-sweep GC: 将死亡对象的内存标记为可分配
  • Mark-compact GC: 移动并整理存活对象(原地整理对象)

思想:根据对象的生命周期选择不同的标记和清理策略

分代GC (Generational GC)

  • 分代假说: most objects die young(很多对象分配出来后就不再使用了)
  • 每个对象都有年龄:经历GC的次数
  • 目的: 针对不同年龄的对象制定不同的GC策略,降低整体内存管理的开销
  • 不同年龄的对象处于heap的不同区域
  • 年轻代:存活对象较少,可以使用Coping-GC,GC吞吐率很高
  • 老年代:对象趋向于一直存活,反复复制开销较大,可使用Mark-sweep GC

引用计数

  • 每个对象都有一个与之关联的引用数目
  • 对象存活条件:当且仅当引用数目大于0
  • 优点:1.内存管理的操作被平摊到程序执行中。 2.内存管理不需要里了解runtime的细节。
  • 缺点:1. 维护引用计数的开销大,要通过原子操作来保证引用计数的原子性和可见性。2.无法回收环形数据结构 3. 额外的内存开销 4. 回收内存仍然可能引发暂停

GO内存管理及优化

go内存管理

  • 分块 提前将内存分块,根据对象大小分配合适的块返回
  • 缓存 每一个p包含一个mcache用于快速分配,用于为绑定在p上的g分配对象,mcache管理一组mspan,mspan没有分配的对象时会缓存在mcentrol,而不是立刻释放并归还给OS

go内存管理优化

  • 对象分配非常高频,每秒分配GB级的内存
  • 小对象占比较高
  • go 内存分配比较耗时,分配路径长:g->m->p->mcache->mspan->memory block->return pointer
  • 优化方案:banlanced gc 本质为将多个小对象的分配合并成一个大对象的分配