周三日一GO -- Go:内存管理和分配

原文地址:https://medium.com/a-journey-with-go/go-memory-management-and-allocation-a7396d430f44

Go 内存管理由标准库自动完成,从内存分配到不再使用时。 虽然开发者不必处理,但 Go 做的底层管理优化得很好,充满了有趣的概念。

在堆中分配内存

内存管理被设计为在并发环境中快速并进行垃圾回收。 让我们从一个简单的例子开始:

package main

type smallStruct struct {
   a, b int64
   c, d float64
}

func main() {
   smallAllocation()
}

//go:noinline
func smallAllocation() *smallStruct {
   return &smallStruct{}
}

注释 //go:noinline 将禁用通过删除函数来优化代码的内联(如果没有该注释会变成内联代码),因此最终没有在堆上分配内存。

通过运行 go tool compile “-m” main.go 来进行内存逃逸分析,确认 Go 的内存分配:

main.go:14:9: &smallStruct literal escapes to heap

转成汇编代码:go tool compile -S main.go,也会明确地向我们展示分配情况:

0x001d 00029 (main.go:14)       LEAQ    type."".smallStruct(SB), AX
0x0024 00036 (main.go:14)       MOVQ    AX, (SP)
0x0028 00040 (main.go:14)       PCDATA  $1, $0
0x0028 00040 (main.go:14)       CALL    runtime.newobject(SB)

函数 newobject 是新分配和代理 mallocgc 的内置函数,该函数在堆上管理它们。 Go 中有两种策略,一种用于较小的分配,一种用于较大的分配。

小内存分配

对于 32kb 以下的小内存分配,Go 将尝试从名为 mcache 的本地缓存中获取内存。 该缓存处理一个名为 mspan 的span列表(32kb 的内存块),其中包含可用于分配的内存:
在这里插入图片描述

每个线程 M 被分配给一个处理器 P 并且一次最多处理一个 goroutine。 在分配内存时,我们当前的 goroutine 将使用其当前 P 的本地缓存来查找 span 列表中可用的第一个空闲对象。 使用这个本地缓存不需要锁定并且使分配更有效(每个goroutine有自己的mcache,如果公用一个mcache则会导致内存分配存在竞争,同时spans大小分块更有效的利用内存)。

span 列表分为 约70 个大小类,从 8 字节到 32k 字节(这个是在应用层设计的吗?那一个新建goroutine不会导致很多的内存分配吗),可以存储不同的对象大小:
在这里插入图片描述

每个spans存在两次:一个不包含指针的对象列表和另一个包含指针的对象列表。 这种区别将使垃圾回收器的工作更轻松,因为它不必扫描不包含任何指针的span。

在我们前面的例子中,结构的大小是 32 字节,将适合 32 字节的span:
在这里插入图片描述

现在,我们可能想知道如果在分配期间没有空闲 span 会发生什么。 Go 维护每个大小不同的span列表,称为 mcentral,其中包含空的span列表和非空span列表两种类型:
在这里插入图片描述

mcentral 维护两个 span列表; 它们中的每一个都有对前一个span和下一个span的引用。 非空列表中的一个span——“非空”意味着列表中至少有一个空闲槽可供分配,可能包含一些已经在使用的内存。 事实上,当垃圾回收器清理内存时,它可以清除一部分span,标记为不再使用的部分,并将其放回非空列表中。

如果槽用完,程序可以从mcentral请求一个span:

在这里插入图片描述

如果空列表中没有可用的span,Go 需要一种方法来将新的span添加到mcentral中。 现在将从堆中分配新的span并链接到mcentral列表:

在这里插入图片描述

堆在需要时从操作系统中提取内存。 如果需要更多内存,堆将分配一大块内存,称为 arena,在64 位体系结构中分配 64Mb,为大多数其他体系结构分配 4Mb。 arena 还将span和内存做了映射关系:
在这里插入图片描述

大内存分配

Go 不使用本地缓存管理大内存分配。 那些大于 32kb 的分配会向上舍入到页面大小,并且这些页面会直接在堆中分配。
在这里插入图片描述

我们现在可以很好地了解内存分配期间发生的事情。 让我们将所有组件绘制在一起以获得完整的图片:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值