这是我参与「第三届青训营 -后端场」笔记创作活动的的第4篇笔记」
性能优化
-
性能优化是什么
- 提升软件系统处理能力,减少不必要的消耗,充分发掘计算机算力
-
为什么要做性能优化
- 用户体验:带来用户体验的提升
- 资源高效利用:降低成本,提高效率——很小的优化乘以海量机器会是显著的性能提升和成本节约
- 程序组件之间调用可能存在不必要的性能消耗
-
优化层面 业务代码-sdk-基础库-语言运行时-os
- 业务层优化 -针对特点场景、具体问题具体分析 -容易获得较大性能收益
- 语言运行时优化
- 考虑更通用的性能问题
- 考虑更多场景
- tradeoffs
- 数据驱动
- 使用自动化性能分析工具
- 非猜测
- 首先优化最大瓶颈
- 优化与质量控制(可维护性)
- 测试用例 尽可能覆盖多的场景 方便回归
- 文档 做了什么没做什么 方便客户决定是否开启优化
- 功能优化可观测:必要的日志输出
自动内存管理
- 动态内存:根据运行时动态分配 malloc()
- 垃圾回收: 由程序语言的运行时系统管理动态内存
- 避免手动内存管理,专注于实现业务逻辑
- 保证内存使用的正确性和安全性
- 三个任务
- 为新对象分配空间
- 找到存活对象(程序还会使用的)
- 回收死亡对象的内存空间
- concurrently
- Mutator:业务线程,分配新对象,修改对象指向关系
- Collector GC线程,找到存活对象,回收死亡对象的内存空间
- 回收算法
- Serial GC:只有一个collector,把mutator都pause,然后用一个collector回收垃圾,回收结束后再继续mutators的执行
- Parallel GC:支持多个collectors同时回收的GC算法
- Concurrent GC:mutator(s)& collector(s)可以同时执行,一边做gc一边做mutator
- o标记了,b没标记(gc过程中业务线程还在执行,创建了新的b对象),但concurrent gc要能感知对象指向关系的改变,把b也标记上(三色标记,混合写膨胀??)
-暂停时间越小越好-用户感知不到最好
tracing garbage collection追踪垃圾回收
- 对象被回收的条件:指针指向关系不可达的对象
- 三步骤
- 标记根对象
- 静态变量、全局变量、常量、线程栈等
- 标记: 找到可达对象
- 求指针指向关系的传递闭包,从根对象出发
- 清理:所有不可达对象,清理策略
- 复制copying GC(复制存活对象到额外开的新空间)
- 标记清理,将死亡对象的内存标记为“可分配” (Marking sweep GC)freelist管理空闲内存
- Mark-compact GC, 移动并整理存活对象(原地整理)类copy但不开新空间
- 根据对象的生命周期,使用不同的标记和清理策略
- 分代GC
-分代假说:大部分对象很快就死了,用完了就回收了
-年龄:经历GC的次数
- 目的:新生代,老年代 制定不同gc策略,降低整体内存管理的开销,
- 新老分别放在两个不同的内存空间中, 不同年龄处于heap的不同区域
- 新生代
- 常规的对象分配
- 存活对象很少,可以copying collection
- gc吞吐率很高
- 老年代
-
趋于一直活着,反复复制开销较大
-
marking-sweep collection
-
- 新生代
- 标记根对象
引用计数
- 每个对象都有一个与之关联的引用数目
- 对象存活的条件:当且仅当引用数大于0
- 优点
- 内存管理的操作被平摊到程序执行过程中,程序边运行 边进行引用计数管理
- 内存管理不需要了解runtime的实现细节:C++智能指针(引用计数的内存管理 自动帮你做)
- 缺点
- 维护引用计数的开销较大,通过原子操作保证引用计数操作的原子性和可见性
- 无法回收环形数据结构
- 内存开销: 每个对象都引入的额外内存空间 存储引用数目
- 回收内存时依然可能引发暂停, null-引用链上是大数据结构
-白色圈 引用不可达-被清理
go内存管理及优化
- 分块,目标:
- 为对象在heap上分配内存
- 提前分块
- 对象分配:根据对象的大小,选择合适的块返回
- gc扫描-指针指向的所有对象扫描
- 缓存
- 多级缓存
- 第一级找不到内存可分配-》去下一级缓存找
- 优化
- 原因, 对象分配是高频操作,小对象占比高-小对象特级优化,内存分配很耗时耗cpu
- 字节方案- balanced GC
小对象移动清理GAB
编译器和静态分析
静态分析:不执行程序代码,推导程序的行为,分析程序的性质
- 控制流 程序执行的流程
- 数据流 数据在控制流上的传递
- 分析得到更多关于程序的性质——进而优化代码
- 过程内分析:仅在函数内部
- 过程间分析,考虑调用时参数传递和返回值的数据流和控制流
Go编译器优化
- 为什么
- 用户无感知
- 通用性优化