这是我参与「第五届青训营 」伴学笔记创作活动的第4天
一、本堂课重点内容
本节课主要从 自动内存管理 Go内存管理和优化 编译器与静态分析与编译器优化,四个方面阐述了如何在go语言发行版中对程序进行优化以及如何进行落地实践
二、详细知识点介绍
自动内存管理
对于自动内存管理,是通过动态内存和自动内存管理(垃圾回收)两种方式进行的
对于molloc, 学过c语言的肯定不陌生, 它相当于把可用内存块用链条串起来(链表),然后当用户去要一块内存的时候,它根据用户要的大小,把链条断开,一份是用户请求的,另一份链接到空闲链表上(如果存在的话)。
总之就三个任务
课件中以这种形式形象地说明了 GC前和过程中的变化
GC算法的评价指标主要有 安全性 吞吐率 暂停时间和内存开销 ,这四种评价指标 的解释说明如下
追踪垃圾回收也是一种自动内存管理的方法
对象被回收的条件:指针指向关系不可达的对象,这个有点类似于树的一些操作,即
- 标记根对象
- 找到可达对象,然后标记
- 清理掉不可达对象,具体步骤是 ①将存活对象复制到另外的内存空间(Copying GC)②将死亡对象的内存标记为“可分配“(Mark-sweep GC) ③移动并整理存活对象(Mark-compact GC),主要的步骤形象化表示如下
具体执行过程中,还需要根据对象的生命周期,使用不同的标记和清除策略。
引用计数的优缺点如下所示
Go内存管理和优化
这一节主要从go内存的分配和go内存的管理优化两部分组成。
go内存分配有两种思想 分块和缓存,分块的目标就是为对象在heap上分配内存,步骤如下 1.提前将内存分块 ,调用mmap()向os申请内存,将申请到的内存分成大块,再将大块分成特定大小的小块,用于对象分配,这些块叫做mspan 分为① noscan mspan:分配不包含指针的对象——GC不需要扫描,② scan mspan:分配包含指针的对象——GC需要扫描 两种类型。 2.对象分配,根据对象的大小,返回最合适的块
第二个思想是缓存 ,go的内存分配借鉴了TCMalloc内存分配器的实现,做了很多级缓存加快其内存分配的速度。Goroutine执行的代码分配一块内存,逻辑处理器(P)有一个数据结构称作mcache里边存在一组mspan,找到最适合的mspan,返回,如果mcache里边的内存块都满了,就会从# mcentral分配一块mspan给mcache,然后返回。具体的名词解释见 www.guoxiaolong.cn/blog/?id=12…
内存分配存在以下问题
- 对象分配是非常高频的操作:每秒分配GB级别的
- 内存小对象占比较高
- Go内存分配比较耗时
- 分配路径长:g ->m ->p -> mcache -> mspan -> memory block -> return pointer. pprof:对象分配的函数是最频繁调用的函数之一
根据上述问题,字节的优化方案是Balanced GC,如下图所示
即按照指针碰撞的方式来分配内存空间,直接操纵通top指针去分配空间,按右边的代码可见,步骤是这样的,拿top指针加上要分配的内存的size 如果不超过g的end 则返回地址
那么这样分配的本质就是将多个小对象的分配合并成一次大对象GAB的分配,就会存在一个问题,如果在g里边有一块内存存活,go内存管理就认为GAB是live的,就会导致GAB被延迟释放。所以加入一个清理操作,GAB总大小如果超过阈值,则重新分配一块GAB,将当前GAB里的对象复制到新GAB,然后清空当前GAB,避免内存泄露,即使用copying GC的算法管理小对象。这么做之后的收益明显。
还讲了关于编译器的概念,以及其静态分析和编译器的优化
四、课后个人总结
本节课学习了一些go语言内存管理和优化的相关操作,还学习了编译器的相关原理,感觉自己对于go语言的内存管理的理解更加透彻了,同时对于之前写过的非go语言的程序有了一定的内存优化的基础。