From - 自由編程
先提一個(gè)問題:一下兩段函數(shù)拾给,外部調(diào)用的時(shí)候,返回值會(huì)發(fā)生值拷貝么?
代碼1
func r1() (rr1 []mnode) {
rr1 = append(rr1, mnode{"h1", 1})
rr1 = append(rr1, mnode{"h2", 2})
return
}
代碼2
func r2() (rr2 []*mnode) {
rr2 = append(rr2, &mnode{"h1", 1})
rr2 = append(rr2, &mnode{"h2", 2})
return
}
用過C語言的同學(xué)谬泌,可能對(duì)代碼1多少有點(diǎn)兒疑問,首先這個(gè)返回值是分配在堆上還是棧上逻谦?返回的時(shí)候掌实,會(huì)拷貝整個(gè)數(shù)組么?
那么我們來分析一下邦马。
大家都知道贱鼻,C語言中,臨時(shí)變量分配在棧中滋将,而通過“new”動(dòng)作分配出來的變量邻悬,則分配在堆中。而Go不同随闽,Go編譯器會(huì)自動(dòng)把有必要的臨時(shí)變量分配在堆中父丰,這個(gè)把臨時(shí)變量分配在堆中的過程,叫做“內(nèi)存逃逸”橱脸。
下面我們來分析一下函數(shù)r1的逃逸過程础米,首先看test.go文件內(nèi)容如下:
package main
import (
"fmt"
)
type mnode struct {
name string
id int
}
func r1() (rr1 []mnode) {
rr1 = append(rr1, mnode{"h1", 1})
rr1 = append(rr1, mnode{"h2", 2})
return
}
func main() {
mr1 := r1()
fmt.Println(mr1)
}
我們通過下面的命令行來進(jìn)行整個(gè)程序的逃逸分析分苇。
go build -gcflags '-m -l' test.go
輸出
# command-line-arguments
./test.go:20:13: main ... argument does not escape
./test.go:20:13: mr1 escapes to heap
這里可以看出,變量mr1已經(jīng)分配在堆上了
簡(jiǎn)單分析一下現(xiàn)在mr1和rr1內(nèi)存分配情況屁桑,把rr1和mr1的地址打印出來看一下
package main
import (
"fmt"
)
type mnode struct {
name string
id int
}
func r1() (rr1 []mnode) {
rr1 = append(rr1, mnode{"h1", 1})
rr1 = append(rr1, mnode{"h2", 2})
fmt.Printf("ptr-rr1: %p \n", &rr1)
fmt.Printf("ptr-rr1[1]: %p \n", &(rr1[1]))
return
}
func main() {
mr1 := r1()
fmt.Println("---------")
fmt.Printf("ptr-mr1: %p \n", &mr1)
fmt.Printf("ptr-mr1[1]: %p \n", &(mr1[1]))
}
然后執(zhí)行 go run test.go医寿,得到如下結(jié)果
$ go run test.go
ptr-rr1: 0xc00000c0a0
ptr-rr1[1]: 0xc000064198
---------
ptr-mr1: 0xc00000c080
ptr-mr1[1]: 0xc000064198
可以看到rr1和mr1的地址確實(shí)不同,但里面元素的地址