golang 性能优化 | 青训营笔记

134 阅读3分钟


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

从Go语言上进行优化

性能优化的目的:提高软件系统的处理能力。

在Go语言基础上优化的方式:内存管理优化和编译器优化

优化的方向:

性能优化.png

Go语言的内存管理

自动内存管理 是对动态内存的管理(垃圾回收),避免手动内存管理,专注业务逻辑,自动内存管理 的主要任务是 为新对象分配空间,找到存活对象,回收死亡对象的内存空间。

Mutator : 业务线程,分配新对象,修改对象指向关系

Colllector : GC线程,找到存活对象,回收死亡对象的内存空间

垃圾收集器

  • Serial GC:只有一个collector。(在垃圾回收时需要暂停业务线程,且为串行模式)

  • Parallel GC:支持多个collectors同时回收的GC算法(同样在垃圾回首时需要暂停业务线程)

  • Concurrent GC :mutator(s)和collector(s)可以同时执行

    Concurrent GC的难度在于collectors必须感知对象指向关系的改变

存活对象判定算法

  • 可达性算法

    1. 标记根对象。静态变量、全局遍历、常量、线程栈等。
    2. 标记可达对象。求指针指向关系的传递闭包(即struct{}下的指针)
    3. 清理所有不可达对象。(标记-整理、标记-清除、标记-复制)
  • 引用计数法。 每个对象都有一个引用计数,当这个对象被引用时引用计数加一,当引用该对象的程序指向完后,引用计数减一。直到引用计数为0时表示对象已死亡。

    引用计数法可以垃圾收集的过程平摊到程序当中。但是维护开销较大;无法回收环形的数据结构,内存开销问题(每个对象都需要一个引用计数),回收内存时依然可能暂停。

GO语言的内存分配

  • 调用mmap() 向OS申请一大块内存,如4MB。

  • 先将内存划分成大块,例如8kb,称作mspan。

  • 再将大块继续划分成特定的小块,用于对象分配。

    noscan mspan:分配不包含指针的对象

    scan mspan:分配包含指针的对象

image-20220514174006761.png 根据对象需要的大小找到最合适的块返回给内存。

GO语言的内存模型如下:

image-20220514174109570.png

优化方案: Balanced GC

背景:堆内存分配的空间主要集中再小内存的分配。

优化方式:每个g都绑定一大块内存(1KB),称作goroutine allocation buffer(GAB);使用三个指针维护。但GAB空间不足的时候再提交GAB。实际上是将小对象合并成一个大对象处理。

编译器与静态分析

静态分析:不执行程序代码,推导程序的执行,分析程序的性质。

控制流:程序执行的流程。

数据流:数据在控制流上的传递。

编译器优化 在于用编译时间换取更高效的机械码。

  • 函数内联:将被调用函数的函数副本替换到调用位置上,同时重写代码以反映参数的绑定。
  • 逃逸分析: "逃逸"是指在对象在当前作用域上由于参数传递等情况传递到另一个作用域。可以通过函数内联等方式解决。

\