Golang雖然是自帶GC的語言,仍然存在內存泄漏的狀況,這片文章總結了Golang中內存泄漏的狀況。git
其中Slice的內存泄漏是最容易中招的,看看這個PR: writev 的 leak,Golang官方都踩了坑。github
本文將就其中的Slice內存泄漏的狀況作分析,並介紹Slice實現和使用的一些關鍵邏輯。golang
Golang是自帶GC的,若是資源一直被佔用,是不會被自動釋放的,好比下面的代碼,若是傳入的slice b
是很大的,而後引用很小部分給全局量a
,那麼b
未被引用的部分就不會被釋放,形成了所謂的內存泄漏。bash
var a []int
func test(b []int) {
a = b[:1]
return
}
複製代碼
想要理解這個內存泄漏,主要就是理解上面的a = b[:1]
是一個引用,其實新、舊slice
指向的都是同一片內存地址,那麼只要全局量a
在,b
就不會被回收。app
關於新、舊slice
指向同一片地址空間,具體能夠看下面的代碼和說明圖,關鍵點在於ui
b:=a[1:3]
時,b
和a
指向了同一片地址上的slice
,b
看到的是索引爲1和2的兩個成員,因此長度爲2, 2也指定了b的讀寫長度。b[0]
的值爲11
,a[1]
的值也會隨之改變,驗證了他們指向同一個地址空間b
的容量爲9,表明了b
引用slice
的真實長度b=a[1:3:2]
,將b
的cap
限制爲2func main() {
a := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
b := a[1:3]
b[0] = 11 // b[0]的改寫,即對a[1]的改寫
fmt.Println(a[1]) // a[1]被寫成了11
fmt.Println(len(a), cap(a)) // 10 10
fmt.Println(len(b), cap(b)) // 2 9
}
複製代碼
若是想避免這個問題,文章頂部的連接裏給出了方法, 它之因此可以從新分配的緣由在於append
方法的實現,若是append的目標slice空間不夠,會從新申請一個array
來放須要append
的內容,因此&b[0]
和&a[0]
的值是不同的,而&a[0]
和&c[0]
地址是一致的:spa
var b []int
var c []int
// 如今,若是再沒有其它值引用着承載着a元素的內存塊,
// 則此內存塊能夠被回收了。
func test(a []int) {
c = a[:1]
b = append(a[:0:0], a[:1]...)
fmt.Println(&a[0], &c[0], &b[0]) //0xc0000aa030 0xc0000aa030 0xc0000b2038
}
複製代碼