批里批里 (゜-゜)つ🍺 干杯~|

七つ一旋桜

园龄:4年2个月粉丝:6关注:3

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

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

性能优化

性能优化可以从两个层面分析

  • 业务层优化
    • 针对特定场景,具体问题具体分析
  • 语言运行时优化
    • 解决更通用(整体业务)的性能问题
    • 需要考虑更多场景
    • 权衡需要被优化的场景

而以上两个层面的优化都需要靠数据驱动去优化

  • 数据驱动

    • 使用pprof等工具分析性能

    • 分析性能要依靠数据而不是猜测

    • 优化性能要优先优化大的性能瓶颈再去优化小的性能瓶颈

优化性能时需要保证代码质量和接口稳定性

保证代码质量可以使用测试驱动开发,每次优化时都行该对接口进行用例测试

go内存分配

go分配内存时会预先调用mmap()向系统申请内存进行分块,分成的块被称为mspan

分出mspan之后会继续把mspan进行分块用于分配

而mspan又分为两种

  • noscan mspan:内存分配给包含指针的对象,gc不需要扫描
  • san mspan:内存分配给包含指针的对象,gc需要扫描

image-20230119145928056

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

image-20230119152439198

gab本身对于go内存分配来说是一个大对象

gab的本质是将多个小对象的内存分配合并成一次大对象的分配

但是由于小对象的内存管理被合并了,这些小对象的内存并不会被及时回收

如何解决这种场景?

方案:当所有的GAB的大小超过一定的阈值之后,就会对每个GAB中的内存进行检查,如果GAB中的对象还存活,就将存活的对象copy到一个新的GAB中,最后再将原来的GAB释放

静态分析

静态分析:不执行代码,推到程序的行为,分析程序的性质

分析程序可以从两点出发

  • 数据流分析和控制流分析

    • 控制流:程序执行的流程

    • 数据流:数据在控制流上的传递

  • 过程内分析和过程外分析

    • 过程内分析是指只在函数内部进行的控制流分析
    • 过程外分析是指考虑函数调用对控制流造成影响的控制流分析(函数的调用会引入新的控制流)

go编译器优化

函数内联

原理:将被调用的函数的函数体的副本替换到调用位置上,同时重写代码以反映参数的绑定

优点:

  • 消除函数调用的开销
  • 将过程见分析转化为过程内分析

缺点:

  • 编译体积变大
  • 编译时间增加

go本身的内敛优化策略比较保守,可以调整函数内联的策略使更多的函数被内敛

逃逸分析

原理:分析代码中指针的动态作用域,指针可以在何处被访问

image-20230119160439322

使用函数内联可以让更多的对象不逃逸,而不逃逸的对象的内存可以在栈上分配

本文作者:七つ一旋桜

本文链接:https://www.cnblogs.com/poifa/p/17716920.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   七つ一旋桜  阅读(21)  评论(0编辑  收藏  举报  
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起