这是我参与「第五届青训营 」伴学笔记创作活动的第 3 天
笔记内容为个人上课对ppt以及自己不了解知识的记录
性能优化与自动内存管理
自动内存管理
概念
-
动态内存,自动内存管理的对象是,动态内存
-
自动内存管理(垃圾回收):由程序语言的运行时系统管理动态内存
- 避免手动内存管理,更加关注业务
- 保证内存使用的安全性
-
三个任务
- 为对象分配空间
- 找到存活的对象
- 回收死亡的对象(不可达)
Mutator:业务线程(用户启动的线程,go routine),分配新对象,修改对象指向关系
Collector: GC线程,找到存活对象,回收死亡对象的内存空间
Serial GC :只有一个collector
Parallel GC :支持多个collectors同时回收的GC算法(并行)
Concurrent GC : mutator(s)和collector(s)可以同时执行(并发)
collector线程做内存的标记和回收
Mutator线程使用内存
tracing garbage collection
- 追踪垃圾回收
对象被回收的条件:指针指向关系不可达的对象
回收流程
标记根对象
静态变量,全局变量,常量,线程栈等
这里的跟对象是指指向堆的指针
标记:找到可达对象
根据指针指向关系,从跟对象出发,找到所有可达对象
因为是指针链,所以能形成链条,最后一个是对象
清理所有不可达对象
将存活对象复制到另外的内存空间
清空原来的大区域
将死亡对象的空间标记为“可分配(Mark-sweep GC)”
白色为可分配空间
移动并整理存活对象(紧凑compact-GC)
策略的选择方式: 根据对象的生命周期,选择不同的标记清理策略
Generational GC
-
分代假说:大多数的对象很快就死掉了
-
Intuition:很多对象在分配出来之后很快就不使用了
-
每个对象都有年龄:经历的GC次数
-
目的:对年轻和老年的对象,制定不同的GC策略,降低整体内存管理的开销
-
不同的年龄的对象,处于heap的不同区域
-
年轻代
-
常规的对象分配
-
由于存活对象很少(很多对象分配出来很快就不用了) ,可以开用copying collection
-
GC吞吐率很高
-
-
老年代
-
对象趋向于一直存活,反复复制开销比较大
-
可以采用mark-sweep collection的方式
(碎片较多时可以使用一次compact-GC进行压缩)
-
引用计数
-
每一个对象(分配的一个heap内存)都有一个与之关联的引用数目
-
对象存活的条件:当且仅当引用数大于0
-
优点
- 内存管理的操作被平摊到程序的执行过程中
- 内存管理不需要了解runtime的实现细节:C++智能指针
-
缺点
- 维护引用计数的开销比较大:通过原子操保证对引用计数操作的原子性和可见性
- 无法回收环形数据结构-----weak reference
- 内存开销:每个对象都要引入额外的内存空间存储引用计数
- 回收内存时仍然可能引发暂停
go内存管理及优化
go内存分配——分块
-
目标:为对象在heap上分配内存
-
提前将内存分块
- 调用系统调用mmap()向os申请一大块内存,例如4MB
- 将内存划分为大块例如8KB,称为mspan
- 再将大块继续划分为小块,用于对象分配
- noscan mspan:分配不包含指针的对象——GC不需要扫描
- scan mspan : 分配包含指针的对象——GC需要扫描TODO
-
对象分配:根据对象的大小,选择最合适的块返回
go内存分配——缓存
-
TCMalloc:thread caching
-
内存分配流程: g—>m—>p—>mcache—>mspan—>memory block—>return pointer
Balanced GC
-
每个g(go routine)都绑定一大块内存(1KB),称作goroutine allocation buffer(GAB)
-
GAB用于noscan类型的小对象分配:<128B
-
使用三个指针维护GAB:base, end, top
-
Bump pointer(指针碰撞)风格对象分配
- 因为GAB是每个goroutine独有的,所以不需要和其他g做互斥分配
- 分配动作简单高效率
-
GAB对于Go内存管理来说,是一个大对象
- 下图:一个mspan中的一个内存对象就是一个gab
-
本质:将多个小对象的分配,合并为一个大对象的分配(大对象已经通过内存分配路径g—>m—>p.....分配完成了)
-
问题:GAB对象分配方式会导致内存被延迟释放:因为go使用标记回收,而大对象内只要有任何一个小对象,都会被标记为在使用,无法回收
-
方案:移动GAB中存活的对象
- 当GAB总大小超过一定域值,就将GAB中存活的对象复制到另外分配的GAB中
- 原先的GAB可以释放,避免内存泄漏
- 本质:用copying GC算法管理小对象,并且减少了内存分配次数
编译器和静态分析
函数内敛
-
Beast Mode
- 函数内涵后拓展了函数边界,更多的对象不逃逸,可以在栈上分配
- GC管理的内存少了,调用频率小了
-
逃逸分析
对象在作用域外能被访问