Go:空花括号对数组初始化内存分配的影响

isa*_*nat 5 arrays static memory-management initialization go

我正在尝试用不同的方式在 golang 中初始化/声明数组。我得到了不同的行为/结果。

go版本go1.3 darwin/amd64

版本1:

func main() {
    a := [100000000]int64{}
    var i int64
    for i = 0; i < 100000000; i++ {
        a[i] = i
    }
}
Run Code Online (Sandbox Code Playgroud)

生成 763MB 的二进制文件。当我使用此消息运行它时,它会在几秒钟后崩溃。

运行时:goroutine 堆栈超过 1000000000 字节限制

致命错误:堆栈溢出

版本2:

func main() {
    var a [100000000]int64
    var i int64
    for i = 0; i < 100000000; i++ {
        a[i] = i
    }
}
Run Code Online (Sandbox Code Playgroud)

生成 456KB 的二进制文件。它运行时间不到一秒。

问题:

谁能帮助我理解为什么存在这些差异(以及我可能错过的其他差异)?谢谢!

编辑:

运行时间:

我构建了两个不同的代码片段并运行编译后的版本,因此没有添加编译时间。相比之下,我第一次运行 version1 的速度非常慢。这是输出。

go build version1.go
go build version2.go
Run Code Online (Sandbox Code Playgroud)

这些是执行输出

版本1

第一次运行

time ./version1
runtime: goroutine stack exceeds 1000000000-byte limit
fatal error: stack overflow

runtime stack:
runtime.throw(0x2fb42a8e)
    /usr/local/Cellar/go/1.3/libexec/src/pkg/runtime/panic.c:520 +0x69
runtime.newstack()
    /usr/local/Cellar/go/1.3/libexec/src/pkg/runtime/stack.c:770 +0x486
runtime.morestack()
    /usr/local/Cellar/go/1.3/libexec/src/pkg/runtime/asm_amd64.s:228 +0x61

goroutine 16 [stack growth]:
main.main()
    /Users/ec/repo/offers/lol/version1.go:3 fp=0x2b7b85f50 sp=0x2b7b85f48
runtime.main()
    /usr/local/Cellar/go/1.3/libexec/src/pkg/runtime/proc.c:247 +0x11a fp=0x2b7b85fa8 sp=0x2b7b85f50
runtime.goexit()
    /usr/local/Cellar/go/1.3/libexec/src/pkg/runtime/proc.c:1445 fp=0x2b7b85fb0 sp=0x2b7b85fa8
created by _rt0_go
    /usr/local/Cellar/go/1.3/libexec/src/pkg/runtime/asm_amd64.s:97 +0x120

goroutine 17 [runnable]:
runtime.MHeap_Scavenger()
    /usr/local/Cellar/go/1.3/libexec/src/pkg/runtime/mheap.c:507
runtime.goexit()
    /usr/local/Cellar/go/1.3/libexec/src/pkg/runtime/proc.c:1445
./version1  0.00s user 0.10s system 1% cpu 7.799 total
Run Code Online (Sandbox Code Playgroud)

第二次运行

runtime: goroutine stack exceeds 1000000000-byte limit
fatal error: stack overflow

runtime stack:
runtime.throw(0x2fb42a8e)
    /usr/local/Cellar/go/1.3/libexec/src/pkg/runtime/panic.c:520 +0x69
runtime.newstack()
    /usr/local/Cellar/go/1.3/libexec/src/pkg/runtime/stack.c:770 +0x486
runtime.morestack()
    /usr/local/Cellar/go/1.3/libexec/src/pkg/runtime/asm_amd64.s:228 +0x61

goroutine 16 [stack growth]:
main.main()
    /Users/ec/repo/offers/lol/version1.go:3 fp=0x2b7b85f50 sp=0x2b7b85f48
runtime.main()
    /usr/local/Cellar/go/1.3/libexec/src/pkg/runtime/proc.c:247 +0x11a fp=0x2b7b85fa8 sp=0x2b7b85f50
runtime.goexit()
    /usr/local/Cellar/go/1.3/libexec/src/pkg/runtime/proc.c:1445 fp=0x2b7b85fb0 sp=0x2b7b85fa8
created by _rt0_go
    /usr/local/Cellar/go/1.3/libexec/src/pkg/runtime/asm_amd64.s:97 +0x120

goroutine 17 [runnable]:
runtime.MHeap_Scavenger()
    /usr/local/Cellar/go/1.3/libexec/src/pkg/runtime/mheap.c:507
runtime.goexit()
    /usr/local/Cellar/go/1.3/libexec/src/pkg/runtime/proc.c:1445
./version1  0.00s user 0.10s system 98% cpu 0.102 total
Run Code Online (Sandbox Code Playgroud)

版本2

第一次运行

time ./version2
./version2  0.16s user 0.26s system 99% cpu 0.429 total
Run Code Online (Sandbox Code Playgroud)

第二次运行

time ./version2
./version2  0.17s user 0.25s system 97% cpu 0.421 total
Run Code Online (Sandbox Code Playgroud)

Jim*_*imB 2

[100000000]int64{}在版本 1 中,您声明了编译器立即分配的文字数组。

版本 2,您仅声明as的类型a[100000000]int64

当您只有变量声明时,在编译过程中此时的内容是未知的。在版本 2 中,编译器知道它的a类型是[100000000]int64,但直到运行时才分配内存。

当您使用文字时,确切的内存表示形式将被写入二进制文件中。string它的工作原理与声明文字与类型变量相同string;字符串文字将就地写入,而变量声明只是一个占位符。

尽管当前的编译器(go 1.3)允许a转义到堆,但文字数据预计会存在于堆栈帧中。您可以在装配输出中看到这一点(框架大小为 800000016):

TEXT    "".func1+0(SB),$800000016-0
Run Code Online (Sandbox Code Playgroud)

如果您实际上需要的文字量大于堆栈所能容纳的文字量,则可以将其放置在全局变量中。以下执行得很好:

var a = [100000000]int64{1}

func func1() {
    var i int64
    for i = 0; i < 100000000; i++ {
        a[i] = i
    }
}
Run Code Online (Sandbox Code Playgroud)

我确实必须在a这里初始化至少一个值,因为如果它等于零值,编译器似乎可以忽略该文字。

  • “但它不需要留出该内存,直到您以某种方式初始化它。” 它*已*初始化。在没有显式初始化的情况下声明变量,会隐式地将其初始化为类型的*零值*,对于数组来说,这会将所有元素初始化为零值,这应该与将其初始化为带有空大括号的文字相同。永远不存在“未初始化的变量”。 (2认同)