怎麼避免內存逃逸?golang
在runtime/stubs.go:133
有個函數叫noescape
。noescape
能夠在逃逸分析中隱藏一個指針。讓這個指針在逃逸分析中不會被檢測爲逃逸。面試
// noescape hides a pointer from escape analysis. noescape is // the identity function but escape analysis doesn't think the // output depends on the input. noescape is inlined and currently // compiles down to zero instructions. // USE CAREFULLY! //go:nosplit func noescape(p unsafe.Pointer) unsafe.Pointer { x := uintptr(p) return unsafe.Pointer(x ^ 0) }
go build -gcflags=-m
查看逃逸的狀況。package main import ( "unsafe" ) type A struct { S *string } func (f *A) String() string { return *f.S } type ATrick struct { S unsafe.Pointer } func (f *ATrick) String() string { return *(*string)(f.S) } func NewA(s string) A { return A{S: &s} } func NewATrick(s string) ATrick { return ATrick{S: noescape(unsafe.Pointer(&s))} } func noescape(p unsafe.Pointer) unsafe.Pointer { x := uintptr(p) return unsafe.Pointer(x ^ 0) } func main() { s := "hello" f1 := NewA(s) f2 := NewATrick(s) s1 := f1.String() s2 := f2.String() _ = s1 + s2 }
執行go build -gcflags=-m main.go
數組
$go build -gcflags=-m main.go # command-line-arguments ./main.go:11:6: can inline (*A).String ./main.go:19:6: can inline (*ATrick).String ./main.go:23:6: can inline NewA ./main.go:31:6: can inline noescape ./main.go:27:6: can inline NewATrick ./main.go:28:29: inlining call to noescape ./main.go:36:6: can inline main ./main.go:38:14: inlining call to NewA ./main.go:39:19: inlining call to NewATrick ./main.go:39:19: inlining call to noescape ./main.go:40:17: inlining call to (*A).String ./main.go:41:17: inlining call to (*ATrick).String /var/folders/45/qx9lfw2s2zzgvhzg3mtzkwzc0000gn/T/go-build763863171/b001/_gomod_.go:6:6: can inline init.0 ./main.go:11:7: leaking param: f to result ~r0 level=2 ./main.go:19:7: leaking param: f to result ~r0 level=2 ./main.go:24:16: &s escapes to heap ./main.go:23:13: moved to heap: s ./main.go:27:18: NewATrick s does not escape ./main.go:28:45: NewATrick &s does not escape ./main.go:31:15: noescape p does not escape ./main.go:38:14: main &s does not escape ./main.go:39:19: main &s does not escape ./main.go:40:10: main f1 does not escape ./main.go:41:10: main f2 does not escape ./main.go:42:9: main s1 + s2 does not escape
其中主要看中間一小段ide
./main.go:24:16: &s escapes to heap //這個是NewA中的,逃逸了 ./main.go:23:13: moved to heap: s ./main.go:27:18: NewATrick s does not escape // NewATrick裏的s的卻沒逃逸 ./main.go:28:45: NewATrick &s does not escape
上段代碼對A
和ATrick
一樣的功能有兩種實現:他們包含一個 string
,而後用 String()
方法返回這個字符串。可是從逃逸分析看ATrick
版本沒有逃逸。函數
noescape()
函數的做用是遮蔽輸入和輸出的依賴關係。使編譯器不認爲 p
會經過 x
逃逸, 由於 uintptr()
產生的引用是編譯器沒法理解的。學習
內置的 uintptr
類型是一個真正的指針類型,可是在編譯器層面,它只是一個存儲一個 指針地址
的 int
類型。代碼的最後一行返回 unsafe.Pointer
也是一個 int
。ui
noescape()
在 runtime
包中使用 unsafe.Pointer
的地方被大量使用。若是做者清楚被 unsafe.Pointer
引用的數據確定不會被逃逸,但編譯器殊不知道的狀況下,這是頗有用的。3d
面試中秀一秀是能夠的,若是在實際項目中若是使用這種unsafe包大機率會被同事打死。不建議使用! 畢竟包的名字就叫作 unsafe
, 並且源碼中的註釋也寫明瞭 USE CAREFULLY!
。指針
中文、數字、英文字母
的字符串