Go:内存管理分配

栏目: IT技术 · 发布时间: 6年前

内容简介::information_source:在内存从分配到回收的生命周期中,内存不再被使用的时候,标准库会自动执行 Go 的内存管理。虽然开发者不必操心这些细节,但是 Go 语言所做的底层管理经过了很好的优化,同时有很多有趣的概念。

Go:内存管理分配

:information_source: 这篇文章基于 Go 1.13。

在内存从分配到回收的生命周期中,内存不再被使用的时候,标准库会自动执行 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)  PCDATA $0, $0
0x0024 00036 (main.go:14)  MOVQ   AX, (SP)
0x0028 00040 (main.go:14)  CALL   runtime.newobject(SB)

函数 newobject 是用于新对象的分配以及代理 mallocgc 的内置函数,该函数在堆上管理这些内存。在 Go 语言中有两种策略,一种用于较小的内存空间的分配,而另一种则用于较大的内存空间的分配。

较小内存空间的分配策略

对于小于 32kb 的,较小的内存空间的分配策略,Go 会从被叫做 mcache 的本地缓存中尝试获取内存。 这个缓存持有一个被叫做 mspan 的内存块(span ,32kb 大小的内存块)列表, mspan 包含着可用于分配的内存:

Go:内存管理分配

每个线程 M 被分配一个处理器 P ,并且一次最多处理一个 goroutine。在分配内存时,当前的 goroutine 会使用它当前的 P 的本地缓存,在 span 链表中寻找第一个可用的空闲对象。使用这种本地缓存不需要锁操作,从而分配效率更高。

span 链表被划分为 8 字节大小到 32k 字节大小的,约 70 个的大小等级,每个等级可以存储不同大小的对象。

Go:内存管理分配

每个 span 链表会存在两份:一个链表用于不包含指针的对象而另一个用于包含指针的对象。这种区别使得垃圾收集器更加轻松,因为它不必扫描不包含任何指针的 span。

在我们前面的例子中,结构体的大小是 32 字节,因此它会适合于 32 字节的 span :

Go:内存管理分配

现在,我们可能会好奇,如果在分配期间 span 没有空闲的插槽会发生什么。Go 维护着每个大小等级的 span 的中央链表,该中央链表被叫做 mcentral ,其中维护着包含空闲对象的 span 和没有空闲对象的 span :

Go:内存管理分配

mcentral 维护着 span 的双向链表;其中每个链表节点有着指向前一个 span 和后一个 span 的引用。非空链表中的 span 可能包含着一些正在使用的内存,“非空”表示在链表中至少有一个空闲的插槽可供分配。当垃圾收集器清理内存时,可能会清理一部分 span,将这部分标记为不再使用,并将其放回非空链表。

我们的程序现在可以在没有插槽的情况下向中央链表请求 span :

Go:内存管理分配

如果空链表中没有可用的 span,Go 需要为中央链表获取新的 span 。新的 span 会从堆上分配,并链接到中央链表上:

Go:内存管理分配

堆会在需要的时候从系统( OS )获取内存,如果需要更多的内存,堆会分配一个叫做 arena 的大块内存,在 64 位架构下为 64Mb,在其他架构下大多为 4Mb。arena 同样适用 span 映射内存。

Go:内存管理分配

较大内存空间的分配策略

Go 并不适用本地缓存来管理较大的内存空间分配。对于超过 32kb 的分配,会向上取整到页的大小,并直接从堆上分配。

Go:内存管理分配

全景图

现在我们对内存分配的时候发生了什么有了更好的认识。现在将所有的组成部分放在一起来得到完整的图画。

Go:内存管理分配

灵感来源

该内存分配最初基于 TCMalloc,一个 Google 创建的,并发环境优化的内存分配器。这个 TCMalloc 的文档 值得阅读;你会发现上面解释过的概念。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Database Design and Implementation

Database Design and Implementation

Edward Sciore / Wiley / 2008-10-24 / 1261.00 元

* Covering the traditional database system concepts from a systems perspective, this book addresses the functionality that database systems provide as well as what algorithms and design decisions will......一起来看看 《Database Design and Implementation》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

MD5 加密
MD5 加密

MD5 加密工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具