后端与 Go 语言内存管理详解 | 青训营笔记

47 阅读3分钟

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

一、第四节课重点内容:

  1. 自动内存管理

  2. Go 内存管理及优化

  3. 编译器和静态分析

  4. Go 编译器优化

二、详细内容:

  • 自动内存管理

    自动内存管理又叫垃圾回收,主要是为了管理动态内存。所以它包括三个任务:1.为新对象分配内存 2.找到存活对象 3.回收死亡对象空间。 从管理过程来看,主要为三个不同部分,用户程序(Mutator)、分配器(Allocator)及 回收器(Collector)(与c/c++不同,严格意义上这哥俩没有GC)。

    具体来说,就是首先需要找出哪些数据还存活着,而哪些数据是不再被需要的(也就是垃圾)。这里可以使用引用计数方法,将没有再被任何东西引用的对象视为可回收垃圾。当对象被创建或者被引用时候内部会存在一个计数器记录它的个数,当它个数为0,即可回收,可参考c++11的智能指针。但同时存在循环引用导致不安全问题(起码c++的share_ptr是这样)。所有又有了追踪垃圾回收。当然还可以使用分代GC。

  • Go内存管理及优化

    Go是基于提前分块的方法(感觉有点像内存池)。

    • Go以Span为单位向系统申请内存,申请到的Span可能只有一个Page,也可能有N个Page。Span中的Page可以被划分为一系列小对象,也可以整体当做中对象或者大对象分配。Go使用了基于tcmalloc的三层框架缓存。结合Go的GMP模型来看,每个mcache作为一个缓存与一个P进行绑定,通过P访问mcache缓存中的对象。当用户程序申请小对象内存时,mcache会查找tcmalloc的三层框架的前端层,有符合条件的就直接返回,否则向中端层申请内存来重新填充(大部分情况下,前端缓存都能满足用户程序的需求)。同时,mcache只支持单线程访问,所以不需要加锁,避免了加锁带来的性能损耗。
    • 由于Go内存分配存在分配路径过长,以及小对象过多问题,优化方案使用Balanced GC(要有balance)。
  • 编译器和静态分析(个人感觉了解就好)

    废话不多说,课件的图一看便知编译器结构。

编译器.png

主要还是对编译器后端优化。老师还讲了会静态分析,即不执行代码,推导程序行为,分析程序性质。
  • Go编译器优化

    主要分为:

    • 函数内联(学过c++的都知道吧)。将被调用函数的函数体的副本替换到调用位置上,同时可以重写代码反映参数的绑定。

    • 逃逸分析,分析代码中指针的动态作用域,即指针何时可以被访问。