[ 后端与 GO内存管理 | 青训营笔记]

60 阅读2分钟

这是我参与[第五届青训营]伴学笔记创作活动的第四天。

1. Golang 内存管理

1.1 准备知识

这里先简单介绍一下 Golang 运行调度。在 Golang 里面有三个基本的概念:G, M, P。

  • G: Goroutine 执行的上下文环境。
  • M: 操作系统线程。
  • P: Processer。进程调度的关键,调度器,也可以认为约等于 CPU。

一个 Goroutine 的运行需要 G + P + M 三部分结合起来。好,先简单介绍到这里,更详细的放在后面的文章里面来说。

1.2 逃逸分析(escape analysis)

对于手动管理内存的语言,比如 C/C++,我们使用 malloc 或者 new 申请的变量会被分配到堆上。但是 Golang 并不是这样,虽然 Golang 语言里面也有 new。Golang 编译器决定变量应该分配到什么地方时会进行逃逸分析。 2. 关键数据结构

几个关键的地方:

  • mcache: per-P cache,可以认为是 local cache。
  • mcentral: 全局 cache,mcache 不够用的时候向 mcentral 申请。
  • mheap: 当 mcentral 也不够用的时候,通过 mheap 向操作系统申请。

可以将其看成多级内存分配器。

2.1 mcache

我们知道每个 Gorontine 的运行都是绑定到一个 P 上面,mcache 是每个 P 的 cache。这么做的好处是分配内存时不需要加锁。 2.2 mspan

span 在 tcmalloc 中作为一种管理内存的基本单位而存在。

  • next, prev: 指针域,因为 mspan 一般都是以链表形式使用。
  • npages: mspan 的大小为 page 大小的整数倍。
  • sizeclass: 0 ~ _NumSizeClasses 之间的一个值,这个解释了我们的疑问。比如,sizeclass = 3,那么这个 mspan 被分割成 32 byte 的块。
  • elemsize: 通过 sizeclass 或者 npages 可以计算出来。比如 sizeclass = 3, elemsize = 32 byte。对于大于 32Kb 的内存分配,都是分配整数页,elemsize = page_size * npages。
  • nelems: span 中包块的总数目。
  • freeindex: 0 ~ nelemes-1,表示分配到第几个块。