Go语言| 青训营笔记

62 阅读4分钟

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

Go 语言内存管理机制

自动内存管理

内存管理是指内存管理是管理计算机内存的过程,在主存和磁盘之间移动进程以提高系统的整体性能。内存管理的基本要求是提供方法来根据程序的请求动态的将部分内存分配给程序,并在不需要时释放它以供重用。

自动内存管理是指由程序语言的运行时系统管理动态内存 通过自动内存管理可以我们可以避免手动释放内存,将注意力专注在业务逻辑,- 保证内存使用的正确性安全性: double-free problem, use-after-free problem

自动内存管理的三个任务:为新对象分配空间 , 找到存活对象, 回收死亡对象的内存空间

相关概念

Mutator: 业务线程,分配新对象,修改对象指向关系

Collector: GC 线程,找到存活对象,回收死亡对象的内存空间

f559f53d773c4d17b0818b5c8651eab4_tplv-k3u1fbpfcp-zoom-in-crop-mark_4536_0_0_0.webp

Serial GC: 只有一个 collector

40e0af197ac4482792910e4fa2d0587a_tplv-k3u1fbpfcp-zoom-in-crop-mark_4536_0_0_0.webp

Parallel GC: 并行 GC,支持多个 collectors 同时回收的 GC 算法

5bc39c15985447adb70edf3e8651a34a_tplv-k3u1fbpfcp-zoom-in-crop-mark_4536_0_0_0.webp Concurrent GC: 并发 GC,支持 mutator(s) 和 collector(s) 同时执行的 GC 算法

54decbf62ce4415e8b20736fa07162d0_tplv-k3u1fbpfcp-zoom-in-crop-mark_4536_0_0_0.webp Collectors 必须感知对象指向关系的改变!

e29f0298f37a4003a941d077741dd10e_tplv-k3u1fbpfcp-zoom-in-crop-mark_4536_0_0_0.webp

垃圾回收

追踪垃圾回收

追踪垃圾回收是指跟踪垃圾回收,其通过循着指针来标识正在使用的、所谓的活动对象。

追踪垃圾回收的回收条件:不可达对象

过程为:

1, - 标记根对象 (GC roots): 静态变量、全局变量、常量、线程栈等

2,- 标记:找到所有可达对象

3,清理:回收所有不可达对象占据的内存空间,具体分为 Copying GC: 将存活对象从一块内存空间复制到另外一块内存空间,原先的空间可以直接进行对象分配 Mark-sweep GC: 将死亡对象所在内存块标记为可分配,使用 free list 管理可分配的空间 Mark-compact GC: 将存活对象复制到同一块内存区域的开头

分代 GC

目的:针对年轻和老年的对象,制定不同的 GC 策略,降低整体内存管理的开销,不同年龄的对象处于 heap 的不同区域。

年轻代 (Young generation):常规的对象分配,由于存活对象很少,可以采用 copying collection,GC 吞叶率很高

老年代 (Old generation):对象趋向于一直活着,反复复制开销较大 ,可以采用 mark-sweep collection

引用计数

每个对象都有一个与之关联的引用数目

对象存活的条件:当且仅当引用数大于 0

优点:

内存管理的操作被平摊到程序运行中:指针传递的过程中进行引用计数的增减

不需要了解 runtime 的细节:因为不需要标记 GC roots,因此不需要知道哪里是全局变量、线程栈等

缺点 :

开销大,因为对象可能会被多线程访问,对引用计数的修改需要原子操作保证原子性和可见性

无法回收环形数据结构

每个对象都引入额外存储空间存储引用计数

虽然引用计数的操作被平摊到程序运行过程中,但是回收大的数据结构依然可能引发暂停

Go 内存管理及优化

Go 内存分配

调用系统调用 mmap() 向 OS 申请一大块内存, 先将内存划分成大块,例如 8 KB,称作 mspan,再将大块继续划分成特定大小的小块,用于对象分配, noscan mspan: 分配不包含指针的对象 (GC 不需要扫描), scan mspan: 分配包含指针的对象 (GC 需要扫描)

缓冲:mcache管理一组mspan,当mcache 中的mspan 分配完毕,向mcentral申请带有未分配块的mspan

Balanced GC

核心:将 noscan 对象在 per-g allocation buffer (GAB) 上分配,并使用移动对象 GC 管理这部分内存,提高对象分配和回收效率

分配对象时,根据对象大小移动 top 指针并返回,快速完成一次对象分配

同原先调用 mallocgc() 进行对象分配的方式相比,balanced GC 缩短了对象分配的路径,减少了对象分配执行的指令数目,降低 CPU 使用

if g.ab.end - g.ab.top < size {
    // Allocate a new allocation buffer
}
addr := g.ab.top
g.ab.top += size
return addr

编译器和静态分析

静态分析:不执行代码,推导程序的行为,分析程序的性质。

控制流:程序的执行流程

数据流:数据在控制流上的传递

Go 编译器优化

函数内联(将被调用函数的函数体的副本替换到调用位置上,同时重写代码以反映参数的绑定)
逃逸分析(分析代码中指针的动态作用域,即指针在何处可以被访问)