这是我参与「第五届青训营 」伴学笔记创作活动的第 6 天
零、前言
本文记录和整理了本堂课:高性能Go语言发行版优化与落地实践,容易遗忘和比较零碎的知识点,撰写本文的目的一是为了参与青训营笔记活动,二是方便自己未来查缺补漏。
一、本堂课重点内容
-
优化
- 内存管理优化
- 编译器优化
-
背景
- 自动管理内存和GO管理内存机制
- 编译器优化的基本问题和思路
二、详细知识点回顾
2.1 自动内存管理
1. 背景
-
动态内存
程序在运行时根据需求产生的内存
-
自动内存管理(垃圾回收)
由程序语言的运行时系统管理动态内存,可以:
- 避免手动管理内存,方便程序员专注于业务逻辑
- 保证内存使用的正确性和安全性
2.相关概念
pause:暂停mutator业务线程,GC线程做相关GC操作,如内存回收。
Concurrent GC则不会产生pause,而是需要做GC操作时唤醒GC线程,一边运行业务线程一边运行GC线程,所以Concurrent GC必须感知对象指向关系的转变
评价GC算法的指标
- 安全性(Safety):不能回收存活的对象 基本要求
- 吞吐率(Throughput):1 - GC时间/程序执行总时间 花在GC操作上的时间
- 暂停时间(pause time):stop the world(STW)
- 内存开销(Space overhead):GC元数据开销
3.追踪垃圾回收
Copying GC:将存活的对象从一个区复制到另一个区域,随后可以清空原有的区域
4.分代GC(Generational GC)
5.引用计数
-
每个对象都有一个与之关联的引用数目
-
对象存活条件:引用计数大于0
-
优点:
优点1:内存管理操作被平摊到程序执行过程中,因为程序一边在执行,内存操作也顺带被执行了:
来看一下这个例子:开始时p指向o,q为空,o引用计数为1;然后p指向o,q指向o,o引用计数为2;最后p为空o,q为空,o引用计数为0,此时对象o可以被释放。
优点2:内存管理不需要了解runtime实现的细节,而只需要维护对象的引用计数,便完成了内存管理:比如C++的智能指针
- 缺点:
缺点1:维护引用计数开销大
缺点2:无法回收环形数据结构
缺点3:额外内存开销:每个对象都需要引入额外内存存储其引用数目
缺点4:回收大数据结构的内存时依然可能引发暂停
2.2 Go内存分配
1. 内存分配--分块
为对象在heap上分配内存:
缓存:
2. GO内存管理优化
- 为什么要进行内存优化
- 对象分配时非常高频的操作:每秒分配几GB的内存
- 小对象占比高
- Go分配内存比较耗时
- 优化防范Balanced GC
GAB对于Go内存管理来说是一个大对象
本质:将多个小对象的分配合并成一次大对象分配
问题:GAB的对象分配会导致内存被延迟释放,一个小对象的存活会使该GAB的存活
解决方案:
2.3 编译器和静态分析
编译器结构
静态分析
静态分析:不执行程序代码,推到程序的行为,分析程序的性质
控制流:(Control flow)程序控制的流程
数据流:(Data flow)数据在控制流上的传递
通过控制流和数据流,我们可以知道更多关于程序的性质:
过程内分析和过程间分析
-
过程内分析(Intra-procedural analysis)
- 仅在函数内部进行分析
-
过程间分析(Inter-procedural analysis)
- 考虑函数调用时参数传递和返回值的数据流和控制流
-
为什么过程间分析是个问题
- 需要数据流分析得知i的具体类型,才能知道i.foo()调用的是哪个foo()
- 根据i的具体类型,产生新的控制流,i.foo(),继续分析
- 过程间分析需要同时考虑控制流和数据流--联合求解,比较麻烦
2.4 Go编译器优化
1. 函数内联
内联:将被调用函数的函数体的副本替换到被调用函数的位置,同时重写代码以反映参数的绑定
- 优点
- 消除函数调用开销
- 将过程间分析转换为过程内分析
2. Beast Mode
Beast Mode:
逃逸分析: