68个spanClass #
在 Go 的三级内存管理器中,维护的对象都是小于 32KB 的小对象。对于这些小对象,Go 又将其按照大小分成了 67 个类别,称为 spanClass。每一个 spanClass 都用来存储固定大小的对象。这 67 个 spanClass 的信息在 runtime.sizeclasses.go 中可以看到详细的说明。
// class bytes/obj bytes/span objects tail waste max waste min align
// 1 8 8192 1024 0 87.50% 8
// 2 16 8192 512 0 43.75% 16
// 3 24 8192 341 8 29.24% 8
// 4 32 8192 256 0 21.88% 32
// ...
// 67 32768 32768 1 0 12.50% 8192
class 3 是说在 spanClass 为 3 的 span 结构中,存储的对象的大小是 24 字节,整个 span 的大小是 8192 字节,也就是一个内存页的大小,可以存放的对象数目最多是 341。
tail waste 这里是 8 字节,这个 8 是通过 8192 mod 24 计算得到,意思是,当这个 span 填满了对象后,会有 8 字节大小的外部碎片。而 max waste 的计算方式则是 [(24−17)×341+8]÷8192 得到,意思是极端场景下,该 span 上分配的所有对象大小都是 17 字节,此时的内存浪费率为 29.24%。
以上 67 个存储小对象的 spanClass 级别,再加上 class 为 0 时用来管理大于 32KB 对象的 spanClass,共总是 68 个 spanClass。
这些数据都是通过在 runtime.mksizeclasses.go 中计算得到的。Go 在分配的时候,是通过控制每个 spanClass 场景下的最大浪费率,来保障堆内存在 GC
时的碎片率的。

Viewpoint #
From #
24 | GC实例:Python和Go的内存管理机制是怎样的?