Here is my go environment:
[lorneli@localhost GoTest]$ go version
go version go1.9 linux/amd64
Here is my program:
package main
type request struct {
ID string
size uint32
off uint64
}
func main() {
r := request{}
iter := interface{}(&r) // &r escapes to heap
iters := make([]interface{}, 0)
iters = append(iters, iter)
}
I allocate a request
instance and convert the pointer of it to interface{}
.
But when analysing with flag -gcflags "-m"
, I found out the instance escapes
to heap when converting. Why this happens?
Here is analysing result:
[lorneli@localhost GoTest]$ go build -gcflags "-m"
# _/mnt/hgfs/vmfolder/workspace/GoTest
./main.go:9:6: can inline main
./main.go:11:21: (interface {})(&r) escapes to heap
./main.go:11:22: &r escapes to heap
./main.go:10:15: moved to heap: r
./main.go:12:15: main make([]interface {}, 0) does not escape
I think this case doesn't match any cases listed on "Go Escape Analysis Flaws".
Simplify your example. Analyze with -gcflags='-m -m'
.
Example 1:
package main
func main() {
var v int
s := make([]*int, 0)
s = append(s, &v) // &v escapes to heap
}
Output:
$ go version
go version devel +df8c2b905b Tue Mar 6 06:13:17 2018 +0000 linux/amd64
$ go run -gcflags='-m -m' esc.go
# command-line-arguments
./esc.go:3:6: can inline main as: func() { var v int; v = <N>; s := make([]*int, 0); s = append(s, &v) }
./esc.go:6:16: &v escapes to heap
./esc.go:6:16: from append(s, &v) (appended to slice) at ./esc.go:6:12
./esc.go:4:6: moved to heap: v
./esc.go:5:11: main make([]*int, 0) does not escape
$
Escape analysis determines whether any references to a value escape the function in which the value is declared. A reference to the variable v
, declared in function main
, escapes as an argument to function append
: &v escapes to heap from append(s, &v)
, moved to heap: v
.
Example 2:
package main
func main() {
var v int
lc := 1
s := make([]*int, lc)
s[0] = &v
}
$ go run -gcflags='-m -m' esc2.go
./esc2.go:3:6: can inline main as: func() { var v int; v = <N>; lc := 1; s := make([]*int, lc); s[0] = &v }
./esc2.go:6:11: make([]*int, lc) escapes to heap
./esc2.go:6:11: from make([]*int, lc) (too large for stack) at ./esc2.go:6:11
./esc2.go:7:9: &v escapes to heap
./esc2.go:7:9: from s[0] (slice-element-equals) at ./esc2.go:7:7
./esc2.go:4:6: moved to heap: v
$
type slice struct {
array unsafe.Pointer
len int
cap int
}
make
for a slice returns a slice descriptor struct
(pointer to underlying array, length, and capacity) and allocates an underlying slice element array. The underlying array is generally allocated on the heap: make([]*int, lc) escapes to heap from make([]*int, lc)
.
s[0] = &v
stores a reference to the variable v
(&v
) in the underlying array on the heap: &v escapes to heap from s[0] (slice-element-equals)
, moved to heap: v
. The reference remains on the heap, after the function ends and its stack is reclaimed, until the underlying array is garbage collected.
If the make
slice capacity is a small (compile time) constant, make([]*int, 1)
in your example, the underlying array may be allocated on the stack. However, escape analysis does not take this into account.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With