为什么fmt.Print中用作参数的变量会被转义?

问题描述 投票:0回答:1

为什么 fmt.Print 中用作参数的变量会被转义? 另外,为什么 print 中用作参数的变量没有转义?

package main

import "fmt"

func main() {
    a := 1
    fmt.Print(a) // a escapes to heap
    print(a)     // a doesn't escape to heap
}
$ go build -gcflags="-m -m -l" main.go
# command-line-arguments
./main.go:7:12: a escapes to heap:
./main.go:7:12:   flow: {storage for ... argument} = &{storage for a}:
./main.go:7:12:     from a (spill) at ./main.go:7:12
./main.go:7:12:     from ... argument (slice-literal-element) at ./main.go:7:11
./main.go:7:12:   flow: {heap} = {storage for ... argument}:
./main.go:7:12:     from ... argument (spill) at ./main.go:7:11
./main.go:7:12:     from fmt.Print(... argument...) (call parameter) at ./main.go:7:11
./main.go:7:11: ... argument does not escape
./main.go:7:12: a escapes to heap
go stack heap
1个回答
-1
投票

fmt.Print: 该函数旨在处理可变数量的参数。在内部,它创建一个切片来保存这些参数。在 Go 中,切片是引用类型,这意味着它们存储底层数据的内存地址。

当你将 a 传递给 fmt.Print 时,Go 需要确保 a 的值在整个函数执行过程中都可以访问。由于 a 是一个小整数,编译器可能会通过将其保留在堆栈上(更快的内存位置)来进行优化。但是,为了保证 fmt.Print 函数内的访问(稍后可能会使用切片),编译器将 a 的值溢出到堆(较慢的内存)并创建对切片内该堆位置的引用。这就是分析显示逃逸到堆的原因。

print: 这是一个带有单个参数的内置函数。它很可能在汇编中实现,并直接使用 a 的值,而不创建任何中间数据结构(如切片)。由于函数可以直接访问堆栈上的 a,因此无需将其转义到堆。

总结:

转义到堆是指在较慢的堆而不是较快的堆栈上分配内存的值。 fmt.Print 使用切片作为参数,这需要引用,可能会导致小变量转义。 print 比较简单,可以直接使用栈上的值。

注意: 此行为可能是特定于编译器的,并且可能会根据各种因素进行不同的优化。您提供的分析是了解潜在内存使用情况的有用工具,但它并不总是性能的明确指标。

© www.soinside.com 2019 - 2024. All rights reserved.