这是我参与「第五届青训营 」笔记创作活动的第 7 天
|| 🎶今日笔记🎶 ||
笔记内容:内存管理
- 自动内存管理
- Go 内存管理及优化
- 编译器和静态分析
- Go 编译器优化
前言 —>
自动内存管理主要管理的是动态内存,动态内存指的是程序在运行时根据需求动态分配的内存,比如C语言中的malloc()函数分配的内存。
自动内存管理也称为垃圾回收,主要目的是由程序语言的运行时系统管理动态内存,这样做有以下两方面的好处:
- 避免手动内存管理,专注于实现业务逻辑
- 保证内存使用的正确性和安全性,比如
C语言中的内存多次释放:double-free problem, 释放后再次使用use-after-free problem。
由此可见,手动释放内存存在很多问题,如果使用不当的话,可能会引起程序的崩溃、漏洞,而自动内存管理可以帮我们自动处理这些问题。
自动内存管理
- 为新对象分配空间
- 找到存活对象
- 回收死亡对象的内存空间
Mutator:业务线程,分配新对象,修改对象指向关系Collector:GC线程,找到存活对象,回收死亡对象的内存空间Serial GC:只有一个collector,会暂停(STW)Parallec GC:支持多个collectors同时回收GC算法,会暂停(STW)Concurrent GC:mutator(s)和collector(s)可以同时执行,Collectors必须感知对象指向关系的改变。
-
Tracing garbage collection: 追踪垃圾回收
- Copying GC: 复制对象 GC
- Mark-sweep GC: 标记-清理 GC
- Mark-compact GC: 标记-压缩 GC
-
Reference counting: 引用计数
-
Generational GC: 分代 GC
- Young generation: 年轻代
- Old generation: 老年代
Concurrent GC进行内存回收时,业务线程和GC线程是同时进行的,所以存在着一定挑战难度,这个挑战主要是GC线程必须感知对象指向关系的改变。
Go 内存管理及优化
- TCMalloc
mmap()系统调用
- scan object 和 noscan object
- mspan, mcache, mentral
- Bump-pointer object allocation: 指针碰撞风格的对象分配
1、可以看到申请内存的时候是以span为单位的,span又分为不同大小,从大小的规律我们可以看到不是简单的按照2次幂进行递增的,是根据计算造成碎片最少的情况下对span的分类,在申请的时候会减少内存碎片。比如在申请47B大小的时候,如果按照2次幂会提供64B大小的内存供应用使用,但是如果按照span会提供48B大小的span,很明显看出,后者造成的碎片会更少。
2、每次从操作系统申请一大块内存,由Go来做分配,减少了系统调用
3、go的内存算法是使用google的TCMalloc内存管理算法,把内存分的非常细,分为多级管理,减少锁的粒度。在回收对象内存时,并没有将其真正释放掉,只是放回预先分配的大块内存中,以便复用。只有内存闲置过多的时候,才会尝试归还部分内存给操作系统,降低整体开销
编译器和静态分析
1· 编译器:
2· 静态
- 静态分析:不执行程序代码,推导程序的行为,分析程序的性质
- 控制流(Control flow):程序执行的流程
- 数据流(Data flow):数据在控制流上的传递
- 通过分析控制流和数据流,我们可以知道更多关于程序的性质(properties)
- 根据这些性质优化代码
Go 编译器优化
-
为什么做编译器优化
- 用户无感知,重新编译即可获得性能收益
- 通用性优化
-
现状
- 采用的优化少
- 编译时间较短,没有进行较复杂的代码分析和优化
-
编译优化的思路
- 场景:面向后端长期执行任务
Tradeoff:用编译时间换取更高效的机器码
-
Beast mode
- 函数内联
- 逃逸分析
- 默认栈大小调整
- 边界检查消除
- 循环展开
- ……
小结:
-
性能优化
- 自动内存管理
- Go内存管理
- 编译器与静态分析
- 编译器优化
-
实践
- Balanced GC优化对象分配
- Beast mode提升代码性能
-
分析问题的方法与解决问题的思路,不仅适用于Go语言,其他语言的优化也同样适用
~ End ~