Golang初识6 | 青训营笔记

56 阅读3分钟

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

涉及语言优化部分的内容较多,今天接着上次的自动内存管理继续探讨Go语言的内存管理与优化,这里主要针对Linux系统。

Go语言内存管理

Go语言的内存分配的基本策略是分块,通过系统调用向系统申请一整块内存后,Go会将这一大块内存分为一些小些的maspan, 每个mspan又会被分为特定大小的最小分配单位,当有新的对象生成时go会为这个对象寻找和它大小最相近的最小分配单元来容纳它,不得不说和OS的内存管理好像。

当然了由于Go语言允许指针的存在,因此为了自动内存管理(GC算法)的方便性,Go会把mspan分为含指针和不含指针的对象,来方便内存管理算法做对象的标记。

同时GO语言设计了自己的多级缓存机制,这加快了go语言的运行速度,方便go语言进行自动内存管理。

Go语言内存管理优化

之所以可以做优化的前提是Go语言的实现是面向General的,所以当我们的业务场景具有某些特征时,Go语言面向General的实现方式对性能是有较大的影响的因此,需要我们自己来实现新的SDK来优化。

第一个问题:

从内存管理上来说,Go并没有考虑不同大小的对象可能占据的内存比例是不同的,以字节的业务为例老师的团队发现在业务上实际被分配的对象都是很小的,因此大量分配小的对象Go分配内存时过长的调用链就会导致很高的系统资源消耗。

从这里出发理所当然可以想到把多次小的分配合并为一次大的分配,就好像提前知道数组的大小一样一次性预分配一整块的空间,比多次去分配空间效率要高很多。

当然这种优化方式也引入了其他的问题,比如某块小空间只剩下一个“活着”的小对象,但由于这一整块空间是分配给一个我们自己设计的对象管理的所以导致这一整块内存都没法被释放(这里类比用户态实现的多线程一个线程阻塞会导致整个进程都被阻塞),因此老师们又设计了新的方法来定时将我们设计的内存分配对象中存活的对象移动到新的最小内存分配对象中,实际上是CopyingGC的方法。

第二个问题:

上面提到过,go内存管理对象有很长的调用链(GMP),为了减少这部分的开销老师的团队通过对我们设计的每个小的内存分配对象增加三个指针(开头,容量位置,目前占用位置),以更短的调用链来获取数据提升服务器的表现。