这是我参与「第五届青训营 」伴学笔记创作活动的第4天,今天学习了GC的一些策略,对追踪垃圾回收和引用计数两种GC类型有了清晰的认知;另外学习了Go怎么管理内存和一些字节的实践;最后了解编译器和静态分析的知识,重点学习了函数内联和逃逸分析。
第一课
性能优化
- 业务层:具体问题,较大性能收益
- 语言层:权衡,多场景
简而言之:数据驱动优化,不要猜
自动内存管理
管理动态内存
目的:1.专注业务2.保证正确性安全性
任务:1.分配 2.找存活对象 3.回收死亡对象
概念
线程分类:Mutator是业务线程,Collector是GC线程
GC分类:serial GC和Parrallel GC会pause,CGC让GC线程和业务线程同时执行
追踪垃圾回收
流程
- 标记根对象(全局,线程栈,常量,静态变量)
- 标记可达对象(跟对象出发,遍历)
- 清理不可达对象
三种清理方式
- Copying GC存活对象复制到另外的内存空间
- Mark sweep GC死亡对象内存标记改为“可分配”
- Mark compact GC移动并整理:拷贝存活对象到开头
分代GC
- 假说:对象分配后很快就不再使用
- 对象年龄:经历过GC的次数
- 策略
年轻代:copyGC,存活对象少
老年代:MarksweepGC,对象一直倾向于活着
引用计数
存活条件:当且仅当引用计数>0
优点:内存管理被平坦到程序执行过程,不管runtime实现细节,如cpp的smart ptr
缺点:环形数据结构不能回收;必须原子操作才能保证引用计数的原子性;大数据结构回收会引起pause
第二课
Go怎么管理内存
基本思路:提前内存分块,每次选择合适大小分配
做法:
- mmap申请一大块内存,划分为大块,大块划分为特定大小的小块
- 缓存:mcache用于快速分配,mcache管理一组mspan。mspan耗尽向mcentral要,mspan为空缓存在mcentral
Go内存管理优化
对象分配高频:每秒分配GB级别的内存
问题:1.小对象多 2.分配路径长
Balanced GC方案
一种指针碰撞风格的GC,分配动作简单
- 小对象偏多,小对象合并为大对象GBA分配,
- 利用copy GC,减少内存延迟释放(大对象GBA里边有一个小对象,占着不能释放)的情况
编译器和静态分析
编译器结构
静态分析
- 数据流分析&&控制流分析:可根据程序性质代码优化
- 过程分析&&过程间分析:复杂
Go编译器优化
通用优化,牺牲编译时间换高效的机器码
函数内联
被调函数副本替换到调用位置 优点:消除函数调用开销 缺点:函数体变大,对于cache不友好,不过绝大情况是正向优化
逃逸分析
指针p 在非当前作用域s的其他区域被访问
总结
有收获,但没有太多实践内容,有点不痛快,赶着做大作业吧。