这是我参与「第三届青训营 -后端场」笔记创作活动的的第5篇笔记
这篇主要感谢张逸飞老师的Go语言内存管理课,可以说是让我印象最深刻的一门课之一了。通过这门课,让我加深了对Go内存管理的相关理解,建议同学们有条件可以再去听听,一定收获很大。
首先,作为一名开发工程师,在性能上的优化可以有效提升软件系统处理能力。其最直接的体现是提高用户体验、高效利用资源、降成本、提效率。
因此,从业务上来说可以梳理业务执行流程,寻找优化点,结合pprof工具具体场景具体分析,进行优化。
众所周知Go有一套完备的基础库提供给我们使用,可以满足大部分场景的需要。但是,针对性能出现相应瓶颈或者想要提高效率时,可以尝试去关注一些开源库。能够有效针对特定场景进行优化,比如JSON解析、比如排序等字节老师们撰写的库。
回到重点:
Go语言本身是一门具有自动内存管理的语言,因此可以减少用户手动申请/释放内存造成的,可以避免重复释放内存/调用释放内存等严重的panic操作。
Go内存管理的相关概念:1.Mutator业务线程也就是我们常说的goroutine协程 2.Collector GC线程主要是找到存活对象,回收死亡对象的进程。 3.串行GC:垃圾回收与工作线程执行区分开来,会产生STW状态。4.并行GC:有多个GC协程同时工作 5.依然会存在STW,但是通过对部分协程进行垃圾回收,尽可能少影响到其他协程的工作
垃圾回收算法:
1.基于追踪的垃圾回收
对象被回收的条件:指针指向关系不可达对象
标记根对象【静态变量、全局变量、常量、线程栈】、
标记:找到可达对象【找指针指向关系的传递闭包,从根对象出发,找到所有可达对象】
清理:所有不可达对象
策略有如下:
1.将存活对象复制到另外的内存空间(Copying GC)
2.将死亡对象内存标记为“可分配”(Mark-sweep GC)通过free list管理
3.移动并整理存货对象(Mark-compact GC) 原地整理对象
【根据对象的生命周期,使用不同的标记和清理策略】
2.引用计数法:
每个对象都有与之关联的引用数目;对象存活条件:当且仅当引用数大于0
优点:
1.内存管理的操作被平坦到程序执行过程中
2.内存管理不需要了解runtime实现细节,C++智能指针
缺点:
1.维护引用计数的开销比较大,需要原子操作保证引用计数的原子性和可见性
2.无法回收环形数据结构 — weak reference
3.内存开销:每个对象都引入的额外内存空间存储数目
4.回收内存时候存在暂停
Go内存管理及优化
分块:
目标:为对象在heap上分配内存,提前将内存分块
提前将内存分块:
1.调用mmap()向OS申请一大块内存,例如4MB
2.将内存划分为大块,例如8KB,称为mspan(1024)
3.将大块分配成特定大小的小块,用于对象分配
4.noscan mspan:分配不包含指针对象 — GC不需要扫描
5.scan mspan:分配包含指针的对象 —GC需要扫描
同时Go内存分配还采用缓存策略,从而加速内存分配:每个p包含一个mcache用于快速分配,用于绑定于p的g分配对象。同时mcache管理一组mspan,如果msapn分配完毕会尝试向mcentral申请未分配的mspan,若干mcentral分配完毕,则再向上一级mheap申请。
栈内存分配
TCMalloc、mmap()系统调用、scan object&noscan object、mspan,mcache,mentral;
Go内存管理初步是这些,后续再结合个人理解再补充一下