Simplify your example. Parse with -gcflags='-m -m' .
Example 1 :
package main func main() { var v int s := make([]*int, 0) s = append(s, &v)
Output:
$ go version go version devel +df8c2b905b Tue Mar 6 06:13:17 2018 +0000 linux/amd64 $ go run -gcflags='-m -m' esc.go
The Escape analysis determines whether any value references can exit the function in which the value is declared. A reference to the variable v declared in the main function is issued as an argument to the append function: &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 handle to the struct fragment (pointer to the base array, length and capacity) and allocates the base array of slice elements. The main array is usually allocated on the heap: make([]*int, lc) escapes to heap from make([]*int, lc) .
s[0] = &v stores the reference to the variable v ( &v ) in the base array on the heap: &v escapes to heap from s[0] (slice-element-equals) , moved to heap: v . The link remains on the heap after the function ends and its stack is fixed until the main array is garbage collected.
If the capacity of the make slice is small (compilation time), make([]*int, 1) in your example, the base array can be allocated on the stack. However, leak analysis does not take this into account.