为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

Go
POPMUISE 2021-08-16 20:10:27
我在用不同的方式在 golang 中初始化/声明数组。我得到了不同的行为/结果。转到版本 go1.3 达尔文/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.gogo build version2.go这些是执行输出版本 1第一次运行time ./version1runtime: goroutine stack exceeds 1000000000-byte limitfatal error: stack overflowruntime stack:runtime.throw(0x2fb42a8e)    /usr/local/Cellar/go/1.3/libexec/src/pkg/runtime/panic.c:520 +0x69runtime.newstack()    /usr/local/Cellar/go/1.3/libexec/src/pkg/runtime/stack.c:770 +0x486runtime.morestack()    /usr/local/Cellar/go/1.3/libexec/src/pkg/runtime/asm_amd64.s:228 +0x61goroutine 16 [stack growth]:main.main()    /Users/ec/repo/offers/lol/version1.go:3 fp=0x2b7b85f50 sp=0x2b7b85f48runtime.main()    /usr/local/Cellar/go/1.3/libexec/src/pkg/runtime/proc.c:247 +0x11a fp=0x2b7b85fa8 sp=0x2b7b85f50runtime.goexit()    /usr/local/Cellar/go/1.3/libexec/src/pkg/runtime/proc.c:1445 fp=0x2b7b85fb0 sp=0x2b7b85fa8created by _rt0_go    /usr/local/Cellar/go/1.3/libexec/src/pkg/runtime/asm_amd64.s:97 +0x120goroutine 17 [runnable]:runtime.MHeap_Scavenger()    /usr/local/Cellar/go/1.3/libexec/src/pkg/runtime/mheap.c:507runtime.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 limitfatal error: stack overflow版本 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 回答

?
缥缈止盈

TA贡献2041条经验 获得超4个赞

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


第2版,你只声明类型的a作为[100000000]int64。


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


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


即使当前的编译器(go 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这里初始化至少一个值,因为如果它等于零值,编译器似乎可以省略这个文字。


查看完整回答
反对 回复 2021-08-16
?
收到一只叮咚

TA贡献1821条经验 获得超4个赞

这实际上不是答案,但也许有人会发现这很有用。


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


type Element struct {

    Parent *Element

    Child []*Element

}

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


所以我只是将“Parent”标记为json:"-"在编组中被忽略。


查看完整回答
反对 回复 2021-08-16
  • 2 回答
  • 0 关注
  • 228 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信