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

时间:2014-09-26 15:36:23

标签: arrays memory-management static go initialization

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

go go go3.3 darwin / amd64

版本1:

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

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

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

致命错误:堆栈溢出

版本2:

func main() {
    var a [100000000]int64
    var i int64
    for i = 0; i < 100000000; i++ {
        a[i] = i
    }
}

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

问题:

任何人都可以帮助我理解为什么那些差异(以及我可能错过的其他差异)存在吗?谢谢!

修改

运行时间:

我构建了两个不同的片段并运行已编译的版本,因此未添加编译时间。虽然我第一次运行version1的速度非常慢。这是输出。

go build version1.go
go build version2.go

这些是执行输出

版本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

第二次运行

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

版本2

首次运行

time ./version2
./version2  0.16s user 0.26s system 99% cpu 0.429 total

第二次运行

time ./version2
./version2  0.17s user 0.25s system 97% cpu 0.421 total

2 个答案:

答案 0 :(得分:1)

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

版本2,您只是将a类型声明为[100000000]int64

如果只有变量声明,则在编译期间不知道内容。在版本2中,编译器知道a的类型为[100000000]int64,但是直到运行时才会分配内存。

使用 literal 时,会将精确的内存表示写入二进制文件。它的工作方式与宣布string文字与string类型的变量相同;字符串文字将写入到位,而变量声明只是一个占位符。

即使当前编译器(转1.3)允许a转义到堆,文字数据也应该存在于堆栈帧中。您可以在程序集输出中看到这一点(帧大小为800000016):

TEXT    "".func1+0(SB),$800000016-0

如果您确实需要比堆栈中更大的文字,则可以将其放在全局变量中。以下执行正常:

var a = [100000000]int64{1}

func func1() {
    var i int64
    for i = 0; i < 100000000; i++ {
        a[i] = i
    }
}

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

答案 1 :(得分:1)

它实际上并不是一个答案,但也许有人会觉得这很有用。

在我的情况下,当我尝试json.Marshal以下结构时会发生这种情况:

type Element struct {
    Parent *Element
    Child []*Element
}

其中Parent指向在Child中具有当前元素的元素。

所以我只是标记&#34;父母&#34;在编组时要忽略json:"-"