The difference is on using the builtin println()
function and the fmt.Println()
function from the standard lib.
println()
is a builtin function and the compiler knows println()
does not retain any arguments passed to it, so arguments passed to it does not escape to heap.
fmt.Prinln()
is from the standard lib and it is handled as any of your custom functions: the compiler makes no assumptions that it does not retain passed arguments, so arguments you pass to it may escape to heap, and so they are allocated on the heap and not on the stack.
This is an important difference. Next what causes the deviation is that Go has dynamic stack: it starts small, and if needed, it can grow. For details, see Does Go have an "infinite call stack" equivalent? Since you pass a considerably large argument to the recursive function stackCopy()
(array of size 1024, element type of int
which is 4 or 8 bytes), the initial small stack will be insufficient, and a larger stack will be allocated, causing stack-allocated variables to be moved, hence their address changing. This won't happen when fmt.Println()
is used, because the compiler deduces s
might escape, so it's allocated on the heap, and growing the stack does not results in moving s
.
You may use the -gcflags '-m'
flags to print the compiler's escape analysis.
In the first case:
./play.go:8:13: inlining call to fmt.Println
./play.go:13:16: s does not escape
./play.go:8:14: "Start" escapes to heap
./play.go:8:13: []interface {} literal does not escape
<autogenerated>:1: .this does not escape
What matters here is: s does not escape
.
In the second case:
./play.go:15:13: inlining call to fmt.Println
./play.go:8:13: inlining call to fmt.Println
./play.go:13:16: leaking param: s
./play.go:15:14: "fmt: " escapes to heap
./play.go:15:30: *s escapes to heap
./play.go:15:13: []interface {} literal does not escape
./play.go:9:2: moved to heap: s
./play.go:8:14: "Start" escapes to heap
./play.go:8:13: []interface {} literal does not escape
<autogenerated>:1: .this does not escape
As you can see, the compiler deduces that s
is a leaking param, *s
escapes to heap.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…