登录
首页 >  Golang >  Go问答

为何 Go 在处理长度为 100k 的切片时比处理长度为 100k 的数组时内存消耗更低?

来源:stackoverflow

时间:2024-02-19 18:00:26 364浏览 收藏

IT行业相对于一般传统行业,发展更新速度更快,一旦停止了学习,很快就会被行业所淘汰。所以我们需要踏踏实实的不断学习,精进自己的技术,尤其是初学者。今天golang学习网给大家整理了《为何 Go 在处理长度为 100k 的切片时比处理长度为 100k 的数组时内存消耗更低?》,聊聊,我们一起来看看吧!

问题内容

考虑以下代码,我分配了 4000 个数组,每个数组长度为 100k:

parentmap := make(map[int][100_000]int)
    for i := 0; i < 4000; i++ {
        parentmap[i] = [100_000]int{}
        time.sleep(3 * time.millisecond)
    }

如果我在本地运行该程序并分析其内存使用情况,它会开始使用 >2gb 内存。

现在,如果我们稍微更改代码以使用数组切片(但长度也为 100k),如下所示:

parentMap := make(map[int][]int)
    for i := 0; i < 4000; i++ {
        parentMap[i] = make([]int, 100_000)
        time.Sleep(3 * time.Millisecond)
    }

在我的机器上,内存峰值约为 73mb。 这是为什么?

我认为这两个片段将使用大致相同的内存,原因如下:

  • 在这两种情况下,go 运行时都会在堆上分配 parentmap 的值。 go 这样做是因为如果它在堆栈上分配这些值,那么一旦当前函数超出范围,parentmap 的值就会全部清除。
  • 因此第一个代码段直接在堆上分配 4k 数组。
  • 并且,第二个片段在堆上分配 4k 切片标头。每个切片头都有一个指向大小为 100k 的唯一数组(也在堆上)的指针。
  • 在这两种情况下,大小为 100k 的堆上都有 4k 数组。因此,在任何一种情况下都应使用大致相等的内存量。

我读到:https://go.dev/blog/slices-intro。但找不到解释这一点的实现细节。


正确答案


带有切片的版本可能受益于延迟分配。没有任何东西会尝试写入这些片之一的数据缓冲区,因此操作系统可以自由地不为这些缓冲区实际分配内存,直到确实尝试写入。 (操作系统也可以延迟对缓冲区进行零初始化,因此不会强制分配。)

同时,带有数组的版本需要将数组实际复制到映射中,这意味着实际执行写入。即使写入的值全为零,它们仍然是写入,因此操作系统必须实际为要写入的数据分配内存。

尝试将数据写入这些切片,切片版本也应该占用千兆字节的内存。 (我认为每页内存一个值应该足够了,但是用 1s 填充切片可能会更容易。)

以上就是本文的全部内容了,是否有顺利帮助你解决问题?若是能给你带来学习上的帮助,请大家多多支持golang学习网!更多关于Golang的相关知识,也可关注golang学习网公众号。

声明:本文转载于:stackoverflow 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>