这是我参与「第五届青训营 」伴学笔记创作活动的第 4 天
课堂笔记(Go语言内存管理详解)
本节课重点内容: 高性能Go语言发行版优化与落地实践
性能优化层面
业务层优化
- 业务代码
- SDK
语言运行时优化
- 基础库
- 语言运行时
- OS
自动内存管理
动态内存
自动内存管理(垃圾回收):由程序语言的运行时系统管理动态内存
- 为新对象分配空间
- 找到存活对象
- 回收死亡对象的内存空间
评价GC算法
- 安全性(Safety):不能回收存活的对象(基本要求)
- 吞吐率(Throughput):1 - GC时间 / 程序执行总时间(花在GC上的时间)
- 暂停时间(Pause time):stop the world(STW)(业务是否感知)
- 内存开销(Space overhead):GC源数据开销
追踪垃圾回收
- Copying GC
- Mark-sweep GC
- Compact GC
分代GC(Generational GC)
引用计数
- 每个对象都有一个阈值关联的引用数目
- 对象存活的条件:当且仅当引用数大于0
优点:
- 内存管理的操作被平摊到程序执行过程中
- 内存管理不需要了解runtime的实现细节:C++智能指针(smart pointer)
缺点:
- 维护引用计数的开销较大:通过原子操作保证对引用计数操作的原子性和可见性
- 无法回收环形结构——weak reference
- 内存开销:每个对象都引入的额外内存空间存储引用数目
- 回收内存时依然可能引发暂停
Go内存管理及优化
Go内存分配
分块
缓存
- 对象分配是非常高频的操作:每秒分配GB级别的内存
- 小对象占比较高
- Go内存分配比较耗时
- 分配路径长:g -> m -> p -> mcache -> mspan -> memory block -> return pointer(过长)
- pprof:对象分配的函数是最频繁调用的函数之一
优化方案Balanced GC
编译器和静态分析
编译器结构
静态分析
- 形态分析:不执行程序代码,推导程序的行为,分析程序的性质
- 控制流(Control flow):程序执行的流程
- 数据流(Data flow):数据在控制流上的传递
- 通过分析控制流和数据流,我们可以知道更多关于程序的性质(properties)
- 根据这些性质优化代码
过程内分析和过程间分析
Go编译器优化
函数内联(lnlining)
- 内联:将被调用的函数体(callee)的副本替换到调用位置上,同时重写代码以反应参数的绑定
优点
-
消除函数调用开销,例如传递参数、保存寄存器等
-
将过程间分析转化为过程内分析,帮助其他优化,例如逃逸分析 缺点
-
函数体变大。instruction cache(icache)不友好
-
编译生成的Go镜像变大
-
函数内联在大多数情况下是正向优化
-
语言特性,例如interface、defer等,限制了函数内联
-
Go语言内联策略非常保守