这是我参与「第三届青训营 -后端场」笔记创作活动的的第3篇笔记。
引言:
1.性能优化的层面
- 业务层优化:应该针对特定场景,具体问题具体分析。
- 语言运行时优化:解决更通用的性能问题。
- 数据驱动:可以通过自动化性能分析工具---pprof,同时进行性能优化应该依靠数据而非猜测,应首先优化最大瓶颈。
2.Go SDK分成两块:接口 实现
在保证接口稳定的前提下改进具体实现。
建议:用测试驱动开发
上述总结:
性能优化的基本问题
性能优化的两个层面:业务层方面、语言运行时优化
性能优化的可维护性
一、自动内存管理
常见的自动内存管理的方式:
- Tracing garbage collection: 追踪垃圾回收
- Reference counting: 引用计数
- Generational GC: 分代 GC
1.自动内存管理的相关概念
- Auto memory management: 自动内存管理
- Grabage collction: 垃圾回收
- Mutator: 业务线程
- Collector: GC 线程
- Concurrent GC: 并发 GC,mutators和collectors可以同时执行
,Collectors必须感知对象指向关系的改变。
- Parallel GC: 并行 GC,会暂停,有多个collector进行回收
- Serial GC: 会暂停,只有一个collector
2.评价GC算法 评价标准:吞吐量、最大暂停时间、堆的使用效率、访问的局部性
3.追踪垃圾回收
分为三个步骤:
①标记根对象
②标记:找到可达对象
③清理:所以不可达的对象
清理时有不同的清理策略:Copying GC(需要额外空间),Mark-sweep GC,mark-compact GC(原地整理)
如何选择不同的清理策略?根据对象的声明周期,选择不同的策略。
4.分代GC 将年龄(经历过GC的次数)不同的GC放置在不同的区域,这其实就是对年轻和老年的对象,制定不同的GC策略,降低整体的内存管理开销。 年轻代(Young generation):存活对象很少,可以采用copying collection。 老年代(Old generation):对象趋向于一直存活,反复复制开销较大,可以采用mask-sweep collection。
5.引用计数
每个对象都有一个与之关联的引用数目,而对象存活的条件是:引用次数>0
二、Go内存管理及优化
前言:基本概念
为方便自动管理内存,可以先向系统预申请一块内存,然后再将内存分割成小块,通过一定的内存分配算法管理内存。
预分配的内存划分为①spans、②bitmap、③arena三部分。其中spans和bitmap都是为了管理arena区而存在的。
①spans区域存放span的指针,每个指针对应一个或多个page,所以span区域的大小为(512G/8KB)*指针大小8byte=512M
②bitmap区域大小也是通过area计算出来,bug主要用于GC
③arena:堆区,应用中所需要的内存从此处分配。arena的大小为512G,将arena区域划分为一个个的page,每个page为8KB,一共有512GB/8KB个页。
1.Go内存分配
Go内存分配的思想:
①分块:目的是为对象在heap上分配内存
mspan分类:noscan mspan---分配不包含指针的对象和scan mspan---分配包含指针的对象
②缓存:借鉴了TCMalloc的思想
2.Go内存管理优化
Go内存分配存在的问题:对象分配是非常高频的操作、小对象占比较高
字节在Go SDK里面针对对象分配方面作出的优化方案:Balanced GC--本质是将多个小对象的分配合并成一次大对象的分配。
Balanced GC的细节:
GAB对于Go内存管理来说是个大对象,而GAB的对象分配方式会导致内存被延迟释放,对此可以移动GAB中存活的对象,也就是说当GAB总大小超过一定的阈值时,将GAB中存活的对象复制到另外分配的GAB中,而原先的GAB可以释放,避免内存泄露;这本质上是用copying GC算法来管理小对象。
三、编译器和静态分析
基本分析、数据流和控制流、过程内和过程间分析
1.基本分析
2.数据流和控制流的概念
- 控制流分析:将程序控制流的信息提取出来,在做控制流分析的时候一般都会用控制流图进行分析
- 数据流分析:分析数据在控制流上的传递
3.静态分析:不执行程序代码,推导程序的行为,分析程序的性质。控制流是指程序执行的流程,数据流是指数据在控制流上的传递。通过分析控制流和数据流,我们可以知道更多关于程序的性质。
4.过程内和过程间分析 过程内:仅在函数内部进行分析。 过程间:考虑函数调用时参数传递和返回值的数据流和控制流。
四、编译器优化
1.Go编译器优化的背景
最大的优点:用户无感知,重新编译就可以获得性能收益
2.函数内联
含义:将被调用函数的函数体的副本替换到调用位置上,同时重写代码以反映参数的绑定。
优点:
- 消除函数的调用开销
- 将过程间分析转化为过程内分析,帮助其他优化,例如逃逸分析。
在进行性能优化时,可以使用micro-benchmark快速验证和对比性能分析结果。
3.逃逸分析
含义:分析代码中指针的动态作用域,指针在何处可以被访问
大致步骤:
①从对象分配处出发,沿着控制流,观察对象的数据流
②若发现指针p在当前作用域s
③则指针p指向的对象逃逸出s,反之则没有逃逸出s