高性能 Go 语言发行版优化与落地实践 | 青训营笔记

139 阅读2分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的的第3篇笔记。

1.自动内存管理

动态内存

自动内存管理

相关概念

Mutator: 业务线程

Collector: GC线程

Serial GC: 只有一个collector

Parallel GC: 支持多个Colletors同时回收的GC算法

Concurrent GC: mutators和collectors可以同时进行 (Collector必须感知对象指向关系的改变)

比如在GC的过程中可能会有新的引用连接上, 这时候就需要通过一些算法来感知这个变化

追踪垃圾回收

对象被回收的条件: 指针指向关系不可达的对象

标记根对象: 比如静态遍历, 全局变量, 常量和线程栈等

标记: 找到可达对象

清理: 所有不可达对象

1.标记-复制 GC

2.标记-清除 GC

3.标记-整理 GC

分代GC

老年代, 年轻代, 使用不同的分配策略和清除策略

根据对象的生命周期, 使用不同的标记和清理策略

2.go内存管理及优化

字节跳动的优化方案 Balanced GC

if top + size <= end {
    addr := top
    top += size
    return addr
}

一个Gab对于Go内存管理来说是一个大对象

本质: 将多个小对象合并成一个大对象

问题: GAB的对象分配方式会导致内存被延迟释放

方案: 移动GAB 中存活的对象

本质: 用标记-复制算法来管理小对象

3.编译器和静态分析

image.png

image.png

4.go编译器优化

函数内联

简单理解就是把函数体内容直接插入到函数调用处,同时还要把实际参数写进去,减小函数调用的开销。

优点:

消除函数调用开销,如传递参数、保存寄存器等 将过程间分析转换为过程内分析,帮助其他优化,例如逃逸分析

缺点:

函数体变大,对于指令缓存不友好

相当于总的指令条数变多了,原来n个函数调用只有固定的一个函数那个多的指令,内联之后需要n个函数量的指令。

编译生成的Go镜像变大

综合优缺点,通过函数规模来控制是否内联,最终一般都是正向收益。

不过Golang中函数内联比较保守,还有一些语言特性(如interface和defer)限制了函数内联。为此字节推出了Beast mode,调整了Golang的内联策略降低了函数调用的开销并增加了逃逸分析的机会,编译时间约增加了10%,Go语言镜像略微增大。