## 問題
知道 golang 的內存逃逸嗎?什么情況下會發生內存逃逸?
## 回答
golang 程序變量會攜帶有一組校驗數據,用來證明它的整個生命周期是否在運行時完全可知。如果變量通過了這些校驗,它就可以在棧上分配。否則就說它 逃逸 了,必須在堆上分配。
能引起變量逃逸到堆上的典型情況:
* **在方法內把局部變量指針返回**局部變量原本應該在棧中分配,在棧中回收。但是由于返回時被外部引用,因此其生命周期大于棧,則溢出。
* **發送指針或帶有指針的值到 channel 中。**在編譯時,是沒有辦法知道哪個`goroutine`會在`channel`上接收數據。所以編譯器沒法知道變量什么時候才會被釋放。
* **在一個切片上存儲指針或帶指針的值。**一個典型的例子就是`[]*string`。這會導致切片的內容逃逸。盡管其后面的數組可能是在棧上分配的,但其引用的值一定是在堆上。
* **slice 的背后數組被重新分配了,因為 append 時可能會超出其容量( cap )。**slice 初始化的地方在編譯時是可以知道的,它最開始會在棧上分配。如果切片背后的存儲要基于運行時的數據進行擴充,就會在堆上分配。
* **在 interface 類型上調用方法。**在 interface 類型上調用方法都是動態調度的 —— 方法的真正實現只能在運行時知道。想像一個 io.Reader 類型的變量 r , 調用 r.Read(b) 會使得 r 的值和切片 b 的背后存儲都逃逸掉,所以會在堆上分配。
## 舉例
**通過一個例子加深理解,接下來嘗試下怎么通過`go build -gcflags=-m`查看逃逸的情況。**
~~~go
package main
import "fmt"
type A struct {
s string
}
// 這是上面提到的 "在方法內把局部變量指針返回" 的情況
func foo(s string) *A {
a := new(A)
a.s = s
return a //返回局部變量a,在C語言中妥妥野指針,但在go則ok,但a會逃逸到堆
}
func main() {
a := foo("hello")
b := a.s + " world"
c := b + "!"
fmt.Println(c)
}
~~~
執行 go build -gcflags=-m main.go
~~~shell
go build -gcflags=-m main.go
# command-line-arguments
./main.go:7:6: can inline foo
./main.go:13:10: inlining call to foo
./main.go:16:13: inlining call to fmt.Println
/var/folders/45/qx9lfw2s2zzgvhzg3mtzkwzc0000gn/T/go-build409982591/b001/_gomod_.go:6:6: can inline init.0
./main.go:7:10: leaking param: s
./main.go:8:10: new(A) escapes to heap
./main.go:16:13: io.Writer(os.Stdout) escapes to heap
./main.go:16:13: c escapes to heap
./main.go:15:9: b + "!" escapes to heap
./main.go:13:10: main new(A) does not escape
./main.go:14:11: main a.s + " world" does not escape
./main.go:16:13: main []interface {} literal does not escape
<autogenerated>:1: os.(*File).close .this does not escape
~~~
* `./main.go:8:10: new(A) escapes to heap`說明`new(A)`逃逸了,符合上述提到的常見情況中的第一種。
* `./main.go:14:11: main a.s + " world" does not escape`說明 b 變量沒有逃逸,因為它只在方法內存在,會在方法結束時被回收。
* `./main.go:15:9: b + "!" escapes to heap`說明 c 變量逃逸,通過`fmt.Println(a ...interface{})`打印的變量,都會發生逃逸,感興趣的朋友可以去查查為什么。
以上操作其實就叫逃逸分析。下篇文章,跟大家聊聊怎么用一個比較 trick 的方法使變量不逃逸。方便大家在面試官面前秀一波。
> 原文[https://mp.weixin.qq.com/s/4YYR1eYFIFsNOaTxL4Q-eQ](https://mp.weixin.qq.com/s/4YYR1eYFIFsNOaTxL4Q-eQ)
- Golnag常見面試題目解析
- 交替打印數組和字母
- 判斷字符串中字符是否全都不同
- 翻轉字符串
- 判斷兩個給定的字符串排序后是否一致
- 字符串替換問題
- 機器人坐標計算
- 語法題目一
- 語法題目二
- goroutine和channel使用一
- 實現阻塞讀的并發安全Map
- 定時與 panic 恢復
- 高并發下的鎖與map讀寫問題
- 為 sync.WaitGroup 中Wait函數支持 WaitTimeout 功能.
- 七道語法找錯題目
- golang 并發題目測試
- 記一道字節跳動的算法面試題
- 多協程查詢切片問題
- 對已經關閉的的chan進行讀寫,會怎么樣?為什么?
- 簡單聊聊內存逃逸?
- 字符串轉成byte數組,會發生內存拷貝嗎?
- http包的內存泄漏