官方社区的解释
"moved to heap" means that a local variable was allocated on the heap
rather than the stack.
"leaking param" means that the memory associated with some parameter
(e.g., if the parameter is a pointer, the memory to which it points)
will escape. This typically means that the caller must allocate that
memory on the heap.
"escapes to heap" means that some value was copied into the heap.
This differs from "moved to heap" in that with "moved to heap" the
variable was allocated in the heap. With "escapes to heap" the value
of some variable was copied, for example when assigning to a variable
of interface type, and that copy forced the value to be copied into a
newly allocated heap slot.
简单来说,
moved to heap
是变量自身在堆上进行分配
leaking param
是和变量相关的内容在堆上进行分配,如果为指针的话,则是其指向的内容在堆上分配
escapes to heap
是和当前变量的一个副本拷贝在堆上进行分配
逃逸分析也会禁止堆到栈的指向,因为函数执行结束后 栈可能被回收,也就导致堆指向未知内容,可能会造成程序崩溃。
如何判断变量是在栈上还是在堆上
注:inheap
判断的是b
所指向的对象是在栈上还是在堆上
// inheap reports whether b is a pointer into a (potentially dead) heap object.
// It returns false for pointers into mSpanManual spans.
// Non-preemptible because it is used by write barriers.
//go:linkname inheap runtime.inheap
func inheap(b uintptr) bool
先从一个简单的例子入手
package main
import (
"fmt"
"unsafe"
_ "unsafe"
)
type myType struct {
a int
b *int
}
//go:noinline
func Test01(m myType) {
fmt.Println(m.a)
fmt.Println(m.b)
println(inheap(uintptr(unsafe.Pointer(&m.a))))
println(inheap(uintptr(unsafe.Pointer(m.b))))
}
//go:noinline
func Test02(m *myType) {
}
//go:linkname inheap runtime.inheap
func inheap(b uintptr) bool
func main() {
a := 1
b := 2
m := myType{a, &b}
Test01(m)
}
go build -gcflags='-m'
./main.go:16:13: inlining call to fmt.Println
./main.go:17:13: inlining call to fmt.Println
./main.go:30:6: can inline main
./main.go:28:13: assuming b is unsafe uintptr
./main.go:15:13: leaking param: m # m leaking param
./main.go:16:15: m.a escapes to heap # m.a escapes to heap
./main.go:16:13: []interface {} literal does not escape
./main.go:17:13: []interface {} literal does not escape
./main.go:23:13: m does not escape
./main.go:32:2: moved to heap: b #b 逃逸到堆上
<autogenerated>:1: .this does not escape
程序运行结果
1
0xc00001c088
false // m.a不在堆上
true // *(m.b)在堆上
可以看出b
(即*m.b
)在堆上分配
m.a escapes to heap
,但m.a
不在堆上
下面再看一个例子
package main
import (
"fmt"
"unsafe"
_ "unsafe"
)
type myType struct {
a int
b *int
}
//go:noinline
func Test02(m *myType) {
fmt.Println(&m)
println(inheap(uintptr(unsafe.Pointer(&m))))
}
//go:linkname inheap runtime.inheap
func inheap(b uintptr) bool
func main() {
a := 1
b := 2
m2 := myType{a, &b}
Test02(&m2)
}
go build -gcflags='-m'
./main.go:16:13: inlining call to fmt.Println
./main.go:21:13: assuming b is unsafe uintptr
./main.go:15:13: moved to heap: m
./main.go:16:13: []interface {} literal does not escape
./main.go:25:2: moved to heap: b
./main.go:26:2: moved to heap: m2
<autogenerated>:1: .this does not escape
0xc00000e028
true
打印m
地址,导致moved to heap: m
package main
import (
"fmt"
"unsafe"
_ "unsafe"
)
type myType struct {
a int
b *int
}
//go:noinline
func Test02(m *myType) {
fmt.Println(m)
println(inheap(uintptr(unsafe.Pointer(&m))))
println(inheap(uintptr(unsafe.Pointer(m.b))))
}
//go:linkname inheap runtime.inheap
func inheap(b uintptr) bool
func main() {
a := 1
b := 2
m2 := myType{a, &b}
Test02(&m2)
}
go build -gcflags='-m'
./main.go:16:13: inlining call to fmt.Println
./main.go:22:13: assuming b is unsafe uintptr
./main.go:15:13: leaking param: m
./main.go:16:13: []interface {} literal does not escape
./main.go:26:2: moved to heap: b
./main.go:27:2: moved to heap: m2
<autogenerated>:1: .this does not escape
&{1 0xc0000b4008}
false
true
打印m
自身,导致leaking param: m
,实际上m.b
逃逸到堆上
package main
import (
"fmt"
"unsafe"
_ "unsafe"
)
type myType struct {
a int
b *int
}
//go:noinline
func Test02(m *myType) {
fmt.Println(m.b)
println(inheap(uintptr(unsafe.Pointer(&m))))
println(inheap(uintptr(unsafe.Pointer(m.b))))
}
//go:linkname inheap runtime.inheap
func inheap(b uintptr) bool
func main() {
a := 1
b := 2
m2 := myType{a, &b}
Test02(&m2)
}
go build -gcflags='-m'
./main.go:16:13: inlining call to fmt.Println
./main.go:22:13: assuming b is unsafe uintptr
./main.go:15:13: leaking param content: m
./main.go:16:13: []interface {} literal does not escape
./main.go:26:2: moved to heap: b
<autogenerated>:1: .this does not escape
0xc0000b4008
false
true
打印m.b
,导致leaking param content: m
,实际上*m.b
逃逸到堆上
leaking param content: m
的含义官方没有给出,推测是说*m
中有内容逃逸到堆上
本文由 LeonardWang 创作,采用 知识共享署名4.0
国际许可协议进行许可
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名
最后编辑时间为: Oct 9,2020