-2

i read the golang FAQ:https://go.dev/doc/faq#stack_or_heap,i want to know when golang allocate variable on stack or heap. so i write code like below :

package main

import (
    "fmt"
)


type Object struct {
    Field int
}

func main() {
    A := Object{1}
    B := Object{2}
    fmt.Println(A,B)
    //fmt.Printf("A:%p;B:%p\n",&A,&B)
    //m := testStackOrHeap()
    //C:=m[A]
    //D:=m[B]
    //fmt.Printf("C:%p;D:%p\n",&C,&D)
}
//go:noinline
func testStackOrHeap() map[Object]Object {
    one:=1
    two:=2
    A := Object{one}
    B := Object{two}
    C:= Object{one}
    D := Object{two}
    fmt.Println(C,D)
    fmt.Printf("A:%p;B:%p\n",&A,&B)
    m := map[Object]Object{A: A, B: B}
    return m
}



then see how the compiler allocate the memory .the cmd is go tool compile "-m" main.go the output is below :

main.go:15:13: inlining call to fmt.Println
main.go:30:13: inlining call to fmt.Println
main.go:31:12: inlining call to fmt.Printf
main.go:15:13: A escapes to heap
main.go:15:13: B escapes to heap
main.go:15:13: []interface {} literal does not escape
main.go:26:2: moved to heap: A
main.go:27:2: moved to heap: B
main.go:30:13: C escapes to heap
main.go:30:13: D escapes to heap
main.go:30:13: []interface {} literal does not escape
main.go:31:12: []interface {} literal does not escape
main.go:32:24: map[Object]Object literal escapes to heap
<autogenerated>:1: .this does not escape

my question is: why not golang allocate variable A B in testStackOrHeap() to the stack ,they can not escape to stackframe ,if it allocate to heap , the gcworker need to collect it,but if it allocate in stack, it will release when function return.

  • The _language_ has no distinction between "heap" and "stack". From a _language_ perspective all variables are the same. Some _implementations_ of Go do differentiate between stack and heap. What is put on the stack and what not is totally implementation dependent and changes regularly. In short: Just because something might be possible to stack allocate doesn't mean it will be stack allocated. The rules are complex and changing. – Volker Nov 30 '21 at 10:49

1 Answers1

0

As @Volker pointed out in a comment, the heap/stack distinction is an implementation detail, and the rules for escape analysis are defined by the compiler, not by the language. Correctness is the most important trait of a compiler, so a compiler's rules will frequently favor simplicity and performance over absolute "optimalness".

In this case, it's quite likely that the compiler doesn't know what fmt.Printf() will do with the pointers it receives. Therefore, it has to assume that the pointers might be stored somewhere on the heap by that function and that the references to those two objects might thus survive the call to testStackOrHeap(). Therefore, it errs on the side of caution and promotes those two variables to the heap.

(Note that your conclusion that they do not escape was presumably based on an assumption that fmt.Printf() won't store the pointers. Did you actually read the source code of that function to learn that it doesn't? If not, you can't actually be sure that it doesn't - just like the compiler isn't sure. And even if the current version of that function doesn't, future versions might.)

Aasmund Eldhuset
  • 37,289
  • 4
  • 68
  • 81
  • 2Thank you very much for your answer, but I still have a question. If AB is allocated on the heap is because of it passed pointers to other functions, but the C and D pointers are not to pass other function(it just pass the value), but they are also allocated on the heap. – Jiangjiang Xu Dec 01 '21 at 02:18
  • @JiangjiangXu: I can't explain those two, and I think that it would be _possible_ for the compiler to prove (by only looking at the code in `testStackOrHeap()`) that they do not escape. Which goes to show that the compiler doesn't have to be as conservative as possible with promoting variables to the heap - all it needs to do is to be completely certain that those variables that it does keep on the stack are guaranteed not to escape, so it might be making simplifications like assuming that anything that is passed to another function (even as a non-pointer) might escape. – Aasmund Eldhuset Dec 01 '21 at 17:52