您当前的位置:首页 > 电脑百科 > 程序开发 > 语言 > Go语言

9 张图就可以弄懂 Go 内存管理

时间:2020-09-24 09:52:14  来源:  作者:
原来 9 张图就可以弄懂 Go 内存管理

Illustration created for “A Journey With Go”, made from the original Go Gopher, created by Renee F

ℹ️ 这篇文章基于 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 包含着可用于分配的内存:

原来 9 张图就可以弄懂 Go 内存管理

 

用 mcache 分配内存

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

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

原来 9 张图就可以弄懂 Go 内存管理

 

span 的大小等级

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

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

原来 9 张图就可以弄懂 Go 内存管理

 

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

原来 9 张图就可以弄懂 Go 内存管理

 

span 的中央链表

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

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

原来 9 张图就可以弄懂 Go 内存管理

 

从 mcentral 中替换 span

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

原来 9 张图就可以弄懂 Go 内存管理

 

从堆上分配 span

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

原来 9 张图就可以弄懂 Go 内存管理

 

堆由 arena 组成

较大内存空间的分配策略

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

原来 9 张图就可以弄懂 Go 内存管理

 

直接从堆上进行大的内存空间分配

全景图

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

原来 9 张图就可以弄懂 Go 内存管理

 

内存分配的组成

灵感来源

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


via: https://medium.com/a-journey-with-go/go-memory-management-and-allocation-a7396d430f44

作者:Vincent Blanchon[2]译者:dust347[3]校对:@unknwon[4]

本文由 GCTT[5] 原创编译,Go 中文网[6] 荣誉推出

参考资料

[1]

TCMalloc 的文档: http://goog-perftools.sourceforge.net/doc/tcmalloc.html

[2]

Vincent Blanchon: https://medium.com/@blanchon.vincent

[3]

dust347: https://github.com/dust347

[4]

@unknwon: https://github.com/unknwon

[5]

GCTT: https://github.com/studygolang/GCTT

[6]

Go 中文网: https://studygolang.com/



Tags:Go语言 内存管理   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
Illustration created for “A Journey With Go”, made from the original Go Gopher, created by Renee Fℹ️ 这篇文章基于 Go 1.13。在内存从分配到回收的生命周期中,内存...【详细内容】
2020-09-24  Tags: Go语言 内存管理  点击:(65)  评论:(0)  加入收藏
▌简易百科推荐
zip 是一种常见的归档格式,本文讲解 Go 如何操作 zip。首先看看 zip 文件是如何工作的。以一个小文件为例:(类 Unix 系统下)$ cat hello.textHello!执行 zip 命令进行归档:$ zip...【详细内容】
2021-12-17  Go语言中文网    Tags:Go语言   点击:(12)  评论:(0)  加入收藏
大家好,我是 polarisxu。前段时间,Russ Cox 明确了泛型相关的事情,原计划在标准库中加入泛型相关的包,改放到 golang.org/x/exp 下。目前,Go 泛型的主要设计者 ianlancetaylor 完...【详细内容】
2021-11-30  Go语言中文网    Tags:slices 包   点击:(24)  评论:(0)  加入收藏
前言最近因为项目需要写了一段时间的 Go ,相对于 Java 来说语法简单同时又有着一些 Python 之类的语法糖,让人大呼”真香“。 但现阶段相对来说还是 Python 写的多一些,偶尔还...【详细内容】
2021-11-25  crossoverJie    Tags:Go   点击:(29)  评论:(0)  加入收藏
go-micro是基于 Go 语言用于开发的微服务的 RPC 框架,主要功能如下:服务发现,负载均衡 ,消息编码,请求/响应,Async Messaging,可插拔接口,最后这个功能牛p安装步骤安装proto...【详细内容】
2021-09-06    石老师小跟班  Tags:go-micro   点击:(196)  评论:(0)  加入收藏
GoLand 2021.2 EAP 5 现已发布。用户可以从工具箱应用程序中获得 EAP 构建,也可以从官方网站手动下载。并且从此 EAP 开始,只有拥有有效的 JetBrains 帐户才能加入该计划。手...【详细内容】
2021-06-29  IT实战联盟  今日头条  Tags:GoLand   点击:(185)  评论:(0)  加入收藏
作者:HDT3213今天给大家带来的开源项目是 Godis:一个用 Go 语言实现的 Redis 服务器。支持: 5 种数据结构(string、list、hash、set、sortedset) 自动过期(TTL) 发布订阅、地理位...【详细内容】
2021-06-18  HelloGitHub  今日头条  Tags:Go   点击:(125)  评论:(0)  加入收藏
统一规范篇合理规划目录本篇主要描述了公司内部同事都必须遵守的一些开发规矩,如统一开发空间,既使用统一的开发工具来保证代码最后的格式的统一,开发中对文件和代码长度的控制...【详细内容】
2021-05-18  1024课堂    Tags:Go语言   点击:(232)  评论:(0)  加入收藏
闭包概述 闭包不是Go语言独有的概念,在很多编程语言中都有闭包 闭包就是解决局部变量不能被外部访问的一种解决方案 是把函数当作返回值的一种应用 代码演示总体思想:在函数...【详细内容】
2021-05-14  HelloGo  今日头条  Tags:Go语言   点击:(223)  评论:(0)  加入收藏
一时想不开,想了解一下Go语言,于是安装了并体验了一下。下载1. 进入golang.google.cn 点击Download Go 2.选择对应的操作系统,点击后开始下载。 安装1. windows下执行傻瓜式安...【详细内容】
2021-05-12  程序员fearlazy  fearlazy  Tags:Go语言   点击:(236)  评论:(0)  加入收藏
1.简介channel是Go语言的一大特性,基于channel有很多值得探讨的问题,如 channel为什么是并发安全的? 同步通道和异步通道有啥区别? 通道为何会阻塞协程? 使用通道导致阻塞的协程...【详细内容】
2021-05-10  程序员麻辣烫  今日头条  Tags:Go通道   点击:(272)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条