Golang 和大内存块分配

Golang and large memory chunk allocation

我有一个相当愚蠢的基准来测试内存分配的效率:

package main

import (
    "time"
    "fmt"
)

func main() {

    size := 1024 * 1024 * 1024

    t := time.Now()
    for j := 0; j < 100; j += 1 {
        a := make([]int, size)
        for i := 0; i < size; i += 1 {
            a[i] = i
        }
        a = nil
    }
    t1 := time.Now()
    fmt.Printf("Duration: %1d", t1.Sub(t).Seconds())
}

使用 16GB 内存的 Mac Pro 大约需要 2-3 分钟,该过程的内存使用量保持在 5-8 GB。

Java 中非常相似的代码占用 3 GB 内存并在 30 秒内完成。

我在这里错过了什么?

What am I missing here?

在Java中,int类型的大小固定为4字节。在 Go 中,int 是一种依赖于架构的类型,在 32 位架构上它是 32 位(4 字节),在 64 位架构上它是 64 位(8 字节)。

您很可能 运行 它在 64 位 arch 上。这意味着您分配的 Go 切片/数组的大小是 8 * 1 GB = 8 GB,而在 Java 中它只有 4 * 1 GB = 4 GB。

此外,由于您在循环中使用 int,Java 只需递增和设置 4 字节值,而在 Go 中您递增和设置 8 字节值(ij 的类型将是 int)。

更改您的 Go 代码以使用 int32,然后再试一次。

另请注意,您的内存使用量测量存在缺陷,因为 Java 中的数组大小在 Go 中为 4 GB 和 8 GB,因此 Java 为 3 GB 和 5-8 GB在 Go 中不是总内存使用量!

还要注意 Go 中的 []int 是一个 slice and not an array, they are not the same. Slices in Go are struct-like headers containing a pointer to a backing array (see reflect.SliceHeader for details), so there is an implicit indirection step involved using them. For details see Also related:

最后一点:您的代码不测量内存分配,因为这只是应用程序执行时间的一小部分。大部分(如 99.99999%)的执行时间是将循环变量递增十亿次并用十亿个元素填充数组。