Day5 go内存管理优化 | 青训营笔记
这是我参与「第五届青训营 」伴学笔记创作活动的第 5 天
性能优化
性能优化可以从两个层面分析
- 业务层优化
- 针对特定场景,具体问题具体分析
- 语言运行时优化
- 解决更通用(整体业务)的性能问题
- 需要考虑更多场景
- 权衡需要被优化的场景
而以上两个层面的优化都需要靠数据驱动去优化
-
数据驱动
-
使用pprof等工具分析性能
-
分析性能要依靠数据而不是猜测
-
优化性能要优先优化大的性能瓶颈再去优化小的性能瓶颈
-
优化性能时需要保证代码质量和接口稳定性
保证代码质量可以使用测试驱动开发,每次优化时都行该对接口进行用例测试
go内存分配
go分配内存时会预先调用mmap()
向系统申请内存进行分块,分成的块被称为mspan
分出mspan
之后会继续把mspan
进行分块用于分配
而mspan又分为两种
- noscan mspan:内存分配给包含指针的对象,gc不需要扫描
- san mspan:内存分配给包含指针的对象,gc需要扫描
go分配内存时会借助TCMalloc
对内存进行缓存,以加速分配内存的速度
流程:
分配内存时会先使用mcache分配一组mspan,然后分配内存时会将mcache中的mspan的内存分配给对象
但如果mcache中的mspan的剩余内存不够用时,mcache会将mcentral(次一级的缓存)中的空闲mspan填充到scache中再分配内存给对象
go内存管理优化
在业务开发中,go的内存分配时非常高频的操作:每秒分配GB几倍的内存
而且大部分对象都是小对象,cpu对这些内存分配的操作过于频繁会导致对性能的影响
如何优化这样的场景?
GAB
预先绑定一块大内存(例如1kb),称作goroutine allocation buffer
gab专门用来分配内存给noscan
类型的小对象
使用三个指针base
,end
, top
来维护gab
gab本身对于go内存分配来说是一个大对象
gab的本质是将多个小对象的内存分配合并成一次大对象的分配
但是由于小对象的内存管理被合并了,这些小对象的内存并不会被及时回收
如何解决这种场景?
方案:当所有的GAB的大小超过一定的阈值之后,就会对每个GAB中的内存进行检查,如果GAB中的对象还存活,就将存活的对象copy到一个新的GAB中,最后再将原来的GAB释放
静态分析
静态分析:不执行代码,推到程序的行为,分析程序的性质
分析程序可以从两点出发
-
数据流分析和控制流分析
-
控制流:程序执行的流程
-
数据流:数据在控制流上的传递
-
-
过程内分析和过程外分析
- 过程内分析是指只在函数内部进行的控制流分析
- 过程外分析是指考虑函数调用对控制流造成影响的控制流分析(函数的调用会引入新的控制流)
go编译器优化
函数内联
原理:将被调用的函数的函数体的副本替换到调用位置上,同时重写代码以反映参数的绑定
优点:
- 消除函数调用的开销
- 将过程见分析转化为过程内分析
缺点:
- 编译体积变大
- 编译时间增加
go本身的内敛优化策略比较保守,可以调整函数内联的策略使更多的函数被内敛
逃逸分析
原理:分析代码中指针的动态作用域,指针可以在何处被访问
使用函数内联可以让更多的对象不逃逸,而不逃逸的对象的内存可以在栈上分配
本文作者:七つ一旋桜
本文链接:https://www.cnblogs.com/poifa/p/17716920.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步