值得注意的是,语言规范中没有出现“堆栈”和“堆”这两个词。是在堆栈上声明的,”和“…在堆上声明的”,但请注意,Go声明语法没有提到堆栈或堆。
从技术上讲,这使得所有问题的答案都依赖于实现。实际上,当然有一个堆栈(每个goroutine!)和一个堆,一些东西放在堆栈上,一些放在堆上。在某些情况下,编译器会遵循严格的规则(比如“new总是在堆上分配”),而在其他情况下,编译器会进行“逃逸分析”,以决定一个对象是否可以驻留在堆栈上,或者是否必须在堆上分配。
在例2中,转义分析将显示指向转义结构的指针,因此编译器必须分配该结构。然而,我认为当前的Go实现在这种情况下遵循了一个严格的规则,即如果一个结构体的任何部分的地址被取走,该结构体就会被放到堆上。
对于问题3,我们可能会对术语感到困惑。围棋中的所有东西都是通过值传递的,没有引用传递。这里返回一个指针值。指针的意义是什么?考虑对您的示例进行以下修改:
type MyStructType struct{}
func myFunction1() (*MyStructType, error) {
var chunk *MyStructType = new(MyStructType)
// ...
return chunk, nil
}
func myFunction2() (MyStructType, error) {
var chunk MyStructType
// ...
return chunk, nil
}
type bigStruct struct {
lots [1e6]float64
}
func myFunction3() (bigStruct, error) {
var chunk bigStruct
// ...
return chunk, nil
}
我修改了myFunction2以返回结构体而不是结构体的地址。现在比较myFunction1和myFunction2的汇编输出,
--- prog list "myFunction1" ---
0000 (s.go:5) TEXT myFunction1+0(SB),$16-24
0001 (s.go:6) MOVQ $type."".MyStructType+0(SB),(SP)
0002 (s.go:6) CALL ,runtime.new+0(SB)
0003 (s.go:6) MOVQ 8(SP),AX
0004 (s.go:8) MOVQ AX,.noname+0(FP)
0005 (s.go:8) MOVQ $0,.noname+8(FP)
0006 (s.go:8) MOVQ $0,.noname+16(FP)
0007 (s.go:8) RET ,
--- prog list "myFunction2" ---
0008 (s.go:11) TEXT myFunction2+0(SB),$0-16
0009 (s.go:12) LEAQ chunk+0(SP),DI
0010 (s.go:12) MOVQ $0,AX
0011 (s.go:14) LEAQ .noname+0(FP),BX
0012 (s.go:14) LEAQ chunk+0(SP),BX
0013 (s.go:14) MOVQ $0,.noname+0(FP)
0014 (s.go:14) MOVQ $0,.noname+8(FP)
0015 (s.go:14) RET ,
不要担心这里的myFunction1输出与peterSO的(极好的)回答不同。我们显然在运行不同的编译器。否则,看到我修改myFunction2返回myStructType而不是*myStructType。对运行时的调用。新事物消失了,这在某些情况下是件好事。等等,这是myFunction3,
--- prog list "myFunction3" ---
0016 (s.go:21) TEXT myFunction3+0(SB),$8000000-8000016
0017 (s.go:22) LEAQ chunk+-8000000(SP),DI
0018 (s.go:22) MOVQ $0,AX
0019 (s.go:22) MOVQ $1000000,CX
0020 (s.go:22) REP ,
0021 (s.go:22) STOSQ ,
0022 (s.go:24) LEAQ chunk+-8000000(SP),SI
0023 (s.go:24) LEAQ .noname+0(FP),DI
0024 (s.go:24) MOVQ $1000000,CX
0025 (s.go:24) REP ,
0026 (s.go:24) MOVSQ ,
0027 (s.go:24) MOVQ $0,.noname+8000000(FP)
0028 (s.go:24) MOVQ $0,.noname+8000008(FP)
0029 (s.go:24) RET ,
仍然没有调用运行时。new,是的,它确实可以按值返回一个8MB的对象。它是有效的,但你通常不想这样做。这里使用指针的目的是避免推入8MB的对象。