在组合字面量部分提到:
获取复合字面量的地址(§Address operators)会生成指向该字面量值实例的唯一指针。
这意味着New
函数返回的指针将是有效的(在堆栈上分配)。
调用:
在函数调用中,函数值和参数按照通常顺序进行评估。
在它们被评估之后,调用的参数按值传递给函数并开始执行调用的函数。
当函数返回时,返回的函数参数按值传回给调用函数。
你可以在这个答案和这个线程中了解更多信息。
正如在"Go中结构体的堆栈分配与堆分配及其与垃圾回收的关系"中所提到的:
值得注意的是,“堆栈”和“堆”这两个词在语言规范中并没有出现。
博客文章“Go中的逃逸分析”详细介绍了发生了什么,并提到了FAQ:
如果可能,Go编译器将在该函数的堆栈帧中分配局部变量。
但是,如果编译器无法证明变量在函数返回后不被引用,则编译器必须在垃圾收集的堆上分配变量以避免悬空指针错误。
另外,如果本地变量非常大,则将其存储在堆上而不是堆栈上可能更有意义。
博客文章还补充说:
The code that does the “escape analysis” lives in src/cmd/gc/esc.c.
Conceptually, it tries to determine if a local variable escapes the current scope; the only two cases where this happens are when a variable’s address is returned, and when its address is assigned to a variable in an outer scope.
If a variable escapes, it has to be allocated on the heap; otherwise, it’s safe to put it on the stack.
Interestingly, this applies to new(T)
allocations as well.
If they don’t escape, they’ll end up being allocated on the stack. Here’s an example to clarify matters:
var intPointerGlobal *int = nil
func Foo() *int {
anInt0 := 0
anInt1 := new(int)
anInt2 := 42
intPointerGlobal = &anInt2
anInt3 := 5
return &anInt3
}
Above, anInt0
and anInt1
do not escape, so they are allocated on the stack;
anInt2
and anInt3
escape, and are allocated on the heap.
请参见"
五个使Go快的方法":
与C不同,它通过
malloc
强制您选择值是存储在堆上还是声明在函数作用域内存储在栈上,Go实现了一种称为逃逸分析的优化。
Go的优化默认始终启用。
您可以使用
-gcflags=-m
开关查看编译器的逃逸分析和内联决策。
由于逃逸分析是在编译时而不是运行时执行的,因此无论您的垃圾回收器效率如何,堆栈分配将始终比堆分配更快。
go build -gcflags'-m'
进行编译以查看优化决策,就可以看到它。 - siritingax := &((2 * 3) + 4)
:https://dev59.com/2l8e5IYBdhLWcg3wlbFR#25601963 - VonC