高性能Go | 青训营笔记

97 阅读3分钟

这是我参与「第五届青训营」伴学笔记创作活动的第 4 天,通过第四天课程的学习,我学习了go内存管理以及垃圾回收的相关知识,掌握了一些内存机制。以下是我的笔记内容。

一、自动内存管理

主要目的:避免手动管理内存,可以让我们更加专注于逻辑业务的实现,保证内存使用的正确性和安全性。

GC算法:

GC算法不能回收存活的对象,并且GC负责回收堆内存,而不负责回收栈中的内存。

吞吐率:1-GC时间/程序执行总时间

根对象:

根对象是垃圾回收在标记过程时最先检查的对象,它包括全局变量、执行栈(包括栈上的变量及指向分配的堆内存区块的指针)、寄存器。

常用垃圾回收算法(根据对象的生命周期,使用不同的标记和清理策略):

引用计数法:

对每个对象维护一个引用计数,当引用该对象的对象被销毁时,引用计数减1,当引用计数器为0时,回收该对象。

优点:对象可以很好的被回收,不会出现内存耗尽。

缺点:对处理循环的引用不太友好,维护也有一定的代价。

标记清除法:

从根变量开始遍历所有引用的对象,引用的对象标记为“被引用”,没有被标记的进行回收。

优点:解决了引用计数的缺点。

缺点:需要STW(STW可以是Stop the World也可以是Start the World的缩写,指代从Stop the World到Start the World这一动作发生时的一段时间间隔)

分代GC

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

二、Go内存管理及优化

分块:

调用系统mmap()向OS申请一大块内存,先将内存划分为大块,之后在划分成特定的大小的小块,用于对象的分配。

优化方案: GAB对于Go内存管理来说是一个对象,本质将多个小对象的分配合并成一次达对象的分配。但是有一个问题:导致内存被延迟释放。那么这个时候就要采用移动GAB中存活的对象来解决,其本质就是用copying GC的算法管理小对象。

三、编译器和静态分析

过程内分析(Intra-procedural analysis):仅在函数内部进行的分析。

过程间分析(Inter-procedural analysis):考虑函数调用时参数传递和返回值的数据流和控制流。

四、Go编译器优化

函数内联:

将被调用函数的函数体的副本替换到调用位置上,同时重写代码来反映参数的绑定。

优点:消除了函数的开销;将过程间分析转化为过程内分析。

缺点:函数体变大,编译生成的Go镜像变大。

Beast Mode:

降低函数调用的开销,增加了逃逸分析。

逃逸分析

逃逸场景:

1.指针逃逸:返回局部变量的指针

2.栈空间不足逃逸

逃逸分析作用:真正做到了按需分配,减小了垃圾回收的压力。

逃逸目的:决定内存分配地址是栈还是堆。

逃逸机制

  • 如果函数外部没有引用,就优先放到栈中;
  • 如果函数外部存在引用,就必定放到堆中;
  • 如果栈上放不下,必定放在堆中。

五、总结

通过今天的学习,我对go垃圾回收机制更加了解,掌握了三色标记和引用计数法,对逃逸分析也有了一定的了解。继续加油!