这是我参与「第五届青训营」伴学笔记创作活动的第 7 天
前言
本文主要介绍:
- 性能优化
- 内存管理以及自动内存管理优化
性能优化
1.性能优化的定义与原因
定义:提升软件系统的处理能力,减少不必要的消耗,充分发掘计算机算力。
原因:
-
用户体验:带来用户体验的提升
- 抖音滑动点击更加顺滑流畅
- 突发事件618,双11,双12购物能够尽可能做到和平时购物一样的流畅度,不卡顿
-
资源高效利用:降低成本,提高效率
- 很小的优化乘以海量机器会显著的性能提升和成本节约
2.两个层面
业务层优化:
- 针对特定场景,具体问题,具体分析
- 容易获得较大性能提升
语言运行优化:
- 解决更通用的性能问题
- 需要考虑更多的场景
- Tradeoffs,折中考虑
3.可维护性
一般来说,优化都是需要写代码,而一般性能优化都是对Go SDK写代码
Go SDK
- 接口层:Commands APIs New APIs
- 实现层: Compiler Scheduler GC Runtime Libs Profiling
质量和可维护性要保证
- 前提:保证接口稳定再进行改进
- 测试用例:覆盖尽可能多的场景,基于正确的软件进行优化
- 文档:将做了什么,能达到什么效果展示给用户,以便用户的个性化使用
- 隔离:通过选项控制是否开启优化
- 可观测:日志输出,告诉用户功能是正常的
内存管理优化
自动内存管理
-
动态内存
- 程序在运行时根据需求动态分配的内存:
malloc()
- 程序在运行时根据需求动态分配的内存:
-
自动内存管理(垃圾回收):由程序语言的运行时系统管理动态内存
- 避免手动管理内存,专注于实现业务逻辑
- 保证内存使用的正确性和安全性:double-free problem,use-after-free problem
-
对于自动内存管理:三个任务
- 为新对象分配空间
- 找到存活对象
- 回收死亡对象的内存空间
相关概念:
- Mutator: 业务线程,分配新对象,修改对象指向关系
- Collector: GC 线程,找到存活对象,回收死亡对象的内存空间
- Serial GC: 只有一个 collector,此时业务线程暂停
- Parallel GC: 支持多个 collectors 同时回收的 GC 算法,此时业务线程暂停
- Concurrent GC: mutator(s) 和 collector(s) 可以同时执行
其中,Collectors必须感知对象指向关系的改变
原因见图,
GC算法的评价指标:
-
安全(Safety):基本要求,不能回收存货的对象
-
吞吐率(throughput):花在gc上的时间,1-GC_time/total_time
-
暂停时间(pause time): STW(stop the world) 业务是否感知
-
内存开销(space overhead) :GC 元数据开销
-
追踪垃圾回收(tracing garbage collection)
-
对象被回收的条件:指针指向关系不可达的对象
-
标记根对象,包括全局变量global、堆heap、栈stack,线程栈等
-
标记:找到可达对象
- 传递闭包,从根对象出发找所有可达对象
-
清理:不可达对象,包括三种方式:
-
将存活对象复制到另外的内存空间(Copying GC)。
- 需要另外的内存空间
-
将死亡对象的内存标记为"可分配”(Mark-sweep GC)。
- 通过free list 管理空闲内存,分配找free list一个内存块即可
-
移动并整理存活对象(Mark-compact GC)
- 原地整理对象(进行压缩,copy在最开始地方)
-
-
-
分代GC(generational GC)
-
分代假说:most objects die young
-
思想:很多对象分配出来之后很快就不用了。
-
每个对象都有一个年龄:与经过GC的次数相关
-
不同年龄的对象分配在不同heap区域。
-
目的:通过制定不同的GC策略,降低整体内存管理的开销
-
对于年轻代
- 常规的对象分配
- 由于一般来说年轻代存活对象很少,用Copying GC处理
- gc吞吐率很高
-
对于老年代
- 对象趋近一直或者,反复赋值开销较大
- 可以采用Mark-compact GC处理
-
-
引用计数(Reference counting)
-
每个对象都有一个与之关联的引用数目
-
圈圈内部数字为引用数目,当引用数目为0时会被清理
-
-
条件(思路):当且仅当引用数大于0时对象存活。
-
优点:
- 内存管理的操作被平摊到程序执行过程中(程序执行过程中就有内存管理)
- 程序进行过程中维护对象的计数,甚至不需要了解runtime的实现细节,如C++智能指针smart pointer
-
缺点:
- 维护上述引用计数的开销比较大:通过原子操作保证引用计数操作的原子性和可见性
- 无法回收环形数据结构,可以通过weak reference解决这个问题
- 内存开销:每个对象都引入的额外内存空间存储引用数目,会使得内存开销变大
- 大数据结构在回收内存时依然可能引发暂停
-
心得
了解性能优化的目的和意义,学习了内存管理以及自动内存管理的相关知识,获益匪浅,都是学校很少细讲的知识。通过从大的自动内存管理观念讲到细小的gc算法,慢慢带你由浅入深地揭开内存管理的面纱。
引用
ppt:高性能 Go 语言发行版优化与落地实践 .pptx - 飞书云文档 (feishu.cn)