Go性能优化|青训营笔记

54 阅读3分钟

这是我参与「第五届青训营 」笔记创作活动的第4天

性能优化的层面

业务层优化

  • 针对特定业务场景
  • 容易获得较大优化收益

语言运行时优化

  • 解决更通用的性能问题
  • 考虑更多场景
  • Tradeoffs

数据驱动

  • 自动化性能分析工具-pprof
  • 依靠数据而非猜测
  • 首先优化最大瓶颈

自动内存管理

背景

  • 动态内存:程序在运行时根据需求分配的内存:malloc()

  • 自动内存管理(垃圾回收): 由程序语言的运行时系统管理动态内存

    • 保证内存使用的安全性正确性
    • 避免手动内存管理
  • 三个任务

    • 为新对象分配空间
    • 找到存活对象
    • 回收死亡对象的内存空间
  • 基本概念

    • Mutator : 业务线程,分配新对象,修改对象指向关系
    • Collector : GC线程,找到存活对象,回收死亡对象的内存空间
    • Serial GC : 只有一个collector的GC算法
    • Parallel GC : 支持多个collectors同时回收的GC算法
    • Concurrent GC : mutator(s)和collector(s)可以同时执行的GC算法

uTools_1674123527485

  • 关于Concurrent GC的难点:

uTools_1674123647527

GC前: O.x指向a

GC过程: 将O,a都标记未存活,但是由于业务线程存活,有可能之后出现一个b被o.y指向 ,所以这是算法的难点

垃圾回收算法

垃圾是指程序向堆栈申请的内存空间,随着程序的运行已经不再使用这些内存空间,这时如果不释放他们就会造成垃圾也就是内存泄漏。

Go中的栈上内存仍由编译器负责管理回收,而堆上的内存由编译器和垃圾收集器负责管理回收。

引用计数算法

每个对象维护一个引用计数,当被引用对象被创建或被赋值给其他对象时引用计数自动 +1。如果这个对象被销毁,那么计数-1,当计数为0时,回收该对象

优点:对象可以很快被回收,不会出现内存耗尽或者达到阈值才回收

缺点:不能很好的处理循环引用

标记-清除((Tracing GC) (go1.3)

从根变量开始遍历所有引用的对象,引用的对象标记“被引用”,没有标记的则进行回收

清理策略:

  • 将存活对象复制到另外空间:copying gc
  • 将死亡对象的内存标记为可分配:Mark-sweep GC,通过free list管理空闲内存
  • 移动并整理存活对象:Mark-compact GC,原地整理对象

优化:先停止STW,再进行垃圾清除

优点:解决了引用计数的缺点

缺点:需要STW(stop the world),暂停程序运行、

分代GC(Generation GC)
  • 分代假说

  • 分配后很快不再使用

  • 每个对象由年龄:经过GC的次数

  • 对年轻和年老对,制定不同GC策略

  • 不同年龄对象处于heap不同区域

  • 年轻代

    • 常规的对象分配
    • 由于存活对象很少,可以使用copying gc
    • GC吞吐率高
  • 老年代

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