Why does a pointer to a local variable go to heap?

Here is my 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 highlight the request instance and convert it to interface{} . But when analyzing with the -gcflags "-m" flag -gcflags "-m" I found that instance screens are a lot when converting. Why is this happening?

The result is analyzed here:

 [ 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 does not correspond to any of the cases indicated in the "Go Escape Analysis Flaws" .

+5
source share
1 answer

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) // &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 $ 

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.

+3
source

Source: https://habr.com/ru/post/1275730/


All Articles