go内存管理与优化|青训营笔记

73 阅读4分钟

这是我参与「第五届青训营」笔记创作活动的第四天,本次课程讲解了go标准库中内存管理和编译器的优化,内存管理能理解,但是对于编译器部分不是很理解,后续重新看过之后再补充笔记

性能优化

  • 是什么:提高软件系统处理能力,减少不必要的消耗,充分发掘算力
  • 目的:资源高效利用,降低成本,提高效率
  • 优化层面:
    • 业务层优化
      • 特定场景和问题
      • 容易获得较大性能收益
    • 语言运行时优化
      • 解决更通用的性能问题
      • 考虑更多场景
    • 数据驱动
      • 依赖数据进行优化
      • pprof
      • 优化最大瓶颈
  • 实际操作对象 Go SDK
    • goSDK 结构image.png
    • 优化时必须保证接口的稳定性
    • 隔离:通过选项控制是否开启优化
    • 测试用例尽可能多的场景
    • 可观测性,必要的日志输出

自动内存管理

  • 基本概念
    • 动态内存
      • 程序在运行时动态分配的内存:malloc
    • 自动内存管理:程序语言运行时系统动态内存
      • 三个任务:
        • 为新对象分配空间
        • 找到存活对象
        • 回收死亡对象空间
      • 相关概念:
        • Mutator:业务线程。分配新对象,修改对象指向
        • Collector:GC线程,找到存活对象,回收死亡对象 -GC算法分类
          • Serial GC:只有一个collector,有暂停
          • parallel GC:多个collectors 同时回收,有赞停
          • Concurrent GC:mutators和collectors同时指向
            • 挑战image.png
          • 分类图解image.png
      • 评价指标
        • 安全性:不能回收存活的对象
        • 吞吐率:1-GC时间/执行总时间 GC更快
        • 暂停时间:stop the world (STW) 业务能否感知
        • 内存开销
      • 常见技术
        • 追踪垃圾回收
          • 步骤:
            • 标记根对象 静态,全局,常量,线程栈等
            • 找到可达对象 找指针指向的传递闭包,从根对象出发
            • 清理不可达对象
              • 清理策略
                • 将存活对象复制到另外的空间 Copying GC
                  • image.png
                • 将死亡对象的内存标记为可分配 Mark-sweep GC
                  • image.png
                • 移动并整理活对象 Mark-compact GC
                  • image.png
            • 策略选择
              • 分代GC
                • 分代假说,大多数objects 很快消亡
                • 每个对象都有年龄:经过GC的代数
                • 目的:对年轻和老年 指定不同GC策略
                • 不同年龄处于heap不同区域
                  • 年轻代
                    • 常规对象分配
                    • 存活对象少,采用copy collection
                    • 吞吐率高
                  • 老年代
                    • 对象趋于一直活着
                      • 可以采用mark-sweep collection
        • 引用计数
          • 每个对象都有与之关联的引用数目
          • 对象存活条件,引用数大于0
          • 优点:
            • 内存管理操作平摊到程序指向过程中
            • 内存管理不需要了解runtime的实现细节
          • 缺点: -开销较大:通过原子操作,保证对引用计数的原子性和可见性
            • 无法回收环形数据结构
            • 内存开销大
            • 回收内存可能引发暂停【回收大数据结果时】

内存管理及优化

go内存分配

  • 目标:对象在heap上分配
  • 实现
    • 提前将内存分块
      • 调用mmap()申请大块内存 eg:4MB
      • 将大内存分成大块 8KB
      • 将大块分成特定大小的小块,用于对象分配
        • noscan mspan 分配不包含指针 --GC不需要扫描
        • scan mspan --GC需要扫描
      • image.png
  • 缓存
    • 一致性原理

go 内存管理优化

  • 对象分配是高频操作
  • 大多数是小对象
  • 分配路径长:g->m->p->mcache->mspan->memory block ->return block

优化方案

  • 每个 g(goroutinue) 都绑定一大块内存(1kb)
  • GAB 用于noscan类型的小对象分配<128B
  • 用三个指针维护GAB:base,end,top
  • 与其他分配请求不互斥
  • 简述image.png
  • GAB 对于go内存来说是一个大对象
  • 本质:将小对象的分配合成为一个大对象分配,减少了分配次数
  • 问题:GAB对象的分配方式会导致内存延迟释放
    • 一个小对象存活导致GAB一直存活image.png
    • 解决方式: 当GAB的总大小超过阈值时,将GAB中存活对象复制到另外分配的GAB中
    • 原本的GAB可以释放
    • 用copying GC 算法管理小对象