【day4】Go 语言内存管理详解 | 青训营笔记

78 阅读4分钟

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

要点:
1.自动内存管理
2.Go 内存管理及优化
3.编译器和静态分析
4.Go 编译器优化

课程1-性能优化及自动内存管理

性能优化的层面:
1.业务代码
2.SDK
3.基础库
4.语言运行时
5.OS

业务层优化:
1.针对特定场景,具体问题,具体分析
2.容易获得较大性能收益

语言运行时优化:
1.解决更通用的性能问题
2.考虑更多场景
3,Tradeoffs

数据驱动
1.自动化性能分析工具——pprof
2.依靠数据而不是猜测
3.首先优化最大瓶颈

1.自动内存管理

三个任务:a.为新对象分配空间;b.找到存活对象;c.回收死亡对象的内存空间

基础概念:

线程分类:
a.Mutator:业务线程,分配新对象、修改对象指向关系等
b.Collector:GC线程,找到存活对象,回收死亡对象的内存空间

GC算法分类:
Serial GC:只有1个collector
Parallel GC:支持多个collectors同时回收的GC算法
Concurrent GC:Mutators 和 Collectors可以同时执行;Collectors必须感知对象指向关系的改变

评价GC算法
安全性:不能回收存活的对象【基本要求】
吞吐率:1-GC时间/程序执行总时间【花在GC上的时间】
暂时时间:stop the world(STW)【GC业务是否感知】
内存开销:GC元数据开销

2.两种GC算法

1.追踪垃圾回收
对象被回收的条件:指针指向关系不可达的对象;
标记根对象:静态变量、全局遍量、常量、线程栈等
标记:找到可达对象
清理:所有不可达对象;

清理策略:
Copying GC:将存活对象复制到另外的内存空间
Mark-sweep GC:将死亡对象的内存标记为“可分配”
Mark-compact GC:移动并整理存活对象【原地整理对象】
可以根据不同的对象存活周期分配不同的清理策略【分代GC】

2.引用计数

每个对象都有一个与之关联的引用数目
对象存活的条件:当且仅当引用数大于0

优点:
a.内存管理操作被平摊到程序执行过程中
b.内存管理不需要了解runtime的实现细节。如C++的智能指针

缺点:
a.维护引用计数的开销较大:需要通过原子操作保证对引用操作的原子性和可见性
b.无法回收环形数据结构——weak reference
c.内存开销:对每个对象都引入额外的内存空间存储引用数目
d.回收内存时依然可能引发暂停

课程2-Go 内存管理 & 编译器优化思路
1.GO内存管理

对象分配时非常高频率的操作;
小对象占比较高
GO内存分配较为耗时:
分配路径:g->m->p->mcache->span->memory block->return pointer
使用pprof查看:对象分配函数是最频繁调用的函数之一

2.编译器优化思路

静态分析:不执行代码,推导程序的行为,分析程序的性质
控制流:程序执行的流程
数据流:数据在控制流上的传递

两个优化思路

函数内联:将函数体考呗到调用位置是那个
优点:
a.消除函数调用开销,如函数传递、保存寄存器等
b.将过程健分析转化为过程内分析,帮助其他优化,如逃逸分析

缺点:
a.函数体变大,对cpu指令cache不友好
B.编译器生成的go的镜像变大

逃逸分析
概念:分析代码中指针的动态作用域:指针在何处可以被访问
大致思路:
a.从对象分配处出发,沿着控制流,观察对象的数据流
b.若发现指针p在当前作用域s:
作为参数传递给其他函数
传递给全局变量
传递给其他goroutine
传递给已经逃逸的指针指向的对象
c.则指针p指向的对象逃逸出s,反之则没有逃逸处