Go语言内存管理详解| 青训营笔记

59 阅读3分钟

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

课堂笔记(Go语言内存管理详解)

本节课重点内容: 高性能Go语言发行版优化与落地实践

性能优化层面

业务层优化

  • 业务代码
  • SDK

语言运行时优化

  • 基础库
  • 语言运行时
  • OS

自动内存管理

动态内存

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

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

image.png

image.png 评价GC算法

  • 安全性(Safety):不能回收存活的对象(基本要求)
  • 吞吐率(Throughput):1 - GC时间 / 程序执行总时间(花在GC上的时间)
  • 暂停时间(Pause time):stop the world(STW)(业务是否感知)
  • 内存开销(Space overhead):GC源数据开销

追踪垃圾回收

image.png

  • Copying GC

image.png

  • Mark-sweep GC

image.png

  • Compact GC

image.png 分代GC(Generational GC)

image.png 引用计数

  • 每个对象都有一个阈值关联的引用数目
  • 对象存活的条件:当且仅当引用数大于0

优点:

  • 内存管理的操作被平摊到程序执行过程中
  • 内存管理不需要了解runtime的实现细节:C++智能指针(smart pointer)

缺点:

  • 维护引用计数的开销较大:通过原子操作保证对引用计数操作的原子性和可见性
  • 无法回收环形结构——weak reference
  • 内存开销:每个对象都引入的额外内存空间存储引用数目
  • 回收内存时依然可能引发暂停

image.png

Go内存管理及优化

Go内存分配

分块 image.png 缓存

image.png

  • 对象分配是非常高频的操作:每秒分配GB级别的内存
  • 小对象占比较高
  • Go内存分配比较耗时
  • 分配路径长:g -> m -> p -> mcache -> mspan -> memory block -> return pointer(过长)
  • pprof:对象分配的函数是最频繁调用的函数之一

优化方案Balanced GC

image.png

image.png

编译器和静态分析

编译器结构

image.png

静态分析

  • 形态分析:不执行程序代码,推导程序的行为,分析程序的性质
  • 控制流(Control flow):程序执行的流程
  • 数据流(Data flow):数据在控制流上的传递
  • 通过分析控制流和数据流,我们可以知道更多关于程序的性质(properties)
  • 根据这些性质优化代码

过程内分析和过程间分析

image.png

Go编译器优化

函数内联(lnlining)

  • 内联:将被调用的函数体(callee)的副本替换到调用位置上,同时重写代码以反应参数的绑定

优点

  • 消除函数调用开销,例如传递参数、保存寄存器等

  • 将过程间分析转化为过程内分析,帮助其他优化,例如逃逸分析 缺点

  • 函数体变大。instruction cache(icache)不友好

  • 编译生成的Go镜像变大

  • 函数内联在大多数情况下是正向优化

  • 语言特性,例如interface、defer等,限制了函数内联

  • Go语言内联策略非常保守

逃逸分析

image.png