后端基础| 青训营笔记

97 阅读5分钟

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

首先就是对昨天的内容进行复习 image.png
然后就进入了主题:
性能优化:
提升软件系统的处理能力,减少不必要的消耗,充分发掘计算机的算力。
为什么要:提升用户的体验,然后资源的高效利用从而降低成本,提高效率。
业务层优化:具体问题具体分析,容易获得较大性能收益
语言运行时优化(对Go的SDK进行优化):解决更通用的性能问题,要考虑更多场景,Tradeoffs
优化的规则
①数据驱动
②依赖pprof工具产生的数据而不是猜测
③首先优化最大瓶颈 image.png
动态内存:程序在运行时根据需求动态分配的内存。 自动内存管理(垃圾回收):由程序语言的运行时系统管理动态内存 (避免手动内存管理,专注于实现业务逻辑) 保证内存使用的正确性和安全性,连续释放两次同样的内存double-free problem,释放内存后使用内存use-after-free problem,可能引入安全漏洞 GC的三个主要任务: 为新对象分配空间,找到存活对象,回收死亡对象的空间。

image.png

image.png

追踪垃圾回收
对象被回收的条件:指针指向关系不可达的对象
标记根对象:静态变量,全局变量,常量,线程栈标记为存活的
标记:找到可达对象
求指针指向关系的传递闭包:从根对象出发,找到所有可达对象。
清理:所有不可达对象
将存活对象复制到另外的内存空间(Copying GC)
将死亡对象的内存标记为"可分配”(Mark-sweep GC)
free list管理起来,然后进行对象分配
移动并整理存活对象(Mark-compact GC)原地整理对象
没有额外的内存空间,将存活的对象拷贝到空间最开始的地方
根据对象的生命周期,使用不同的标记和清理策略

分代GC
分代假说:大多数的对象很快就死掉了
来源:很多对象在分配出来之后很快就不再使用了
每个对象都有年龄:经历过GC的次数
目的:对年轻和老年的对象,制定不同的GC策略,降低整体内存管理的开销
不同年龄的对象处于heap的不同区域
年轻代(Young generation)
①常规的对象分配 ②由于存活对象很少,可以采用 copying collection ③GC 吞吐率很高
老年代(Old generation)
对象趋向于一直活着,反复复制开销较大可以采用 mark-sweep collection

引用计数:每个对象都有一个与之关联的引用数目
对象存活的条件:当且仅当引用数大于0
内存管理的操作被平摊到程序执行过程中
内存管理不需要了解 runtime 的实现细节: C++ 智能指针 (smart pointer
缺点
维护引用计数的开销较大: 通过原子操作保证对引用计数操作的原子性和可见性无法回收环形数据结构-但是可以用weak reference进行处理
内存开销: 每个对象都引入的额外内存空间存储引用数目
回收内存时依然可能引发暂停-回收大树结构的时候

image.png

Go的内存优化 Go是提前将对象分配,然后在内存中找到最接近的内存块进行分配。
第一部分: image.png
第二部分 image.png
对象分配是非常高频的操作:每秒分配GB级别的内存
小对象占比比较高,且go的分配路径长
字节自己的优化方案:Balanced GC
image.png
分配8bit的小对象:top指针向前移动8bit,然后把内存返回出去,就完成内存的分配。
GAB对于Go内存管理来说是一个大对象,然后本质就是将多个小对象的分配合并成一次大对象的分配,这里面会产生的问题为:GAB的对象分配方式会导致内存被延迟释放。
方案:当GAB的总大小超过一定阈值时,将GAB中存活中的对象用copying GC的算法复制到另外分配的GAB上,然后原先的GAB可以释放,避免内存的泄漏。
性能收益:缩短了对象分配的周期,所以CPU usage会降低,然后由于内存分配加快了,所以核心接口时延也会相应下降。

image.png
静态分析:不执行程序代码,推导程序的行为,分析程序的性质。
控制流:程序执行的流程(控制流图CFG)
数据流:数据在控制流上的传递(数据流图)。
过程内分析:函数内部分析。
过程间分析:考虑函数调用时参数传递和返回值的数据流和控制流(联合求解)

编译器优化:用户无感知,重新编译就可以获得性能收益。通用的优化。
思路
场景:面向后端长期执行任务。
TRadeoff:用编译时间换取更高效的机器码。
集成在Go SDK中的Beast mode

image.png
函数内联:将被调用函数的函数体(callee)的副本调换到调用位置(caller)上,同时重写代码以反映参数的绑定。
优点:消除函数开销比如,传递参数,保存寄存器等。将过程间分析转化为过程内分析。
缺点:函数体变大,instruction cache(icache即CPU的指令)不友好,编译生成的Go镜像变大,大多数是正向优化。
逃逸分析:分析代码中指针的动态作用域:指针在何处可以被访问。
从对象分配处出发,沿着控制流,观察对象的数据量。
如果指针P在当前作用域S作为参数传递给①其他函数②传递给全局变量③其他的goroutine④已逃逸的指针指向的对象,则p指向的对象逃逸出s。
Beast mode优化:未逃逸的对象可以在栈上分配(内存使用率降低),对象在栈上分配和回收很快,移动sp。减少在heap上的分配,降低GC负担。

image.png