Golang雖然是自帶GC的語言,仍然存在內存洩漏的情況,這篇文章總結了Golang中內存洩漏的情況。
其中Slice的內存洩漏是最容易中招的,看看這個PR:https://github.com/golang/go/pull/32138,Golang官方都踩了坑。
本文將就其中的Slice內存洩漏的情況做分析,並介紹Slice實現和使用的一些關鍵邏輯。
Golang是自帶GC的,如果資源一直被佔用,是不會被自動釋放的,比如下面的代碼,如果傳入的slice b是很大的,然後引用很小部分給全局量a,那麼b未被引用的部分就不會被釋放,造成了所謂的內存洩漏。
var a []int
func test(b []int) { a = b[:1] return}想要理解這個內存洩漏,主要就是理解上面的a = b[:1]是一個引用,其實新、舊slice指向的都是同一片內存地址,那麼只要全局量a在,b就不會被回收。
關於新、舊slice指向同一片地址空間,具體可以看下面的代碼和說明圖,關鍵點在於
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限制為2
func main() { a := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} b := a[1:3] b[0] = 11 fmt.Println(a[1]) fmt.Println(len(a), cap(a)) fmt.Println(len(b), cap(b)) }如果想避免這個問題,文章頂部的連結裡給出了方法, 它之所以能夠重新分配的原因在於append方法的實現,如果append的目標slice空間不夠,會重新申請一個array來放需要append的內容,所以&b[0]和&a[0]的值是不一樣的,而&a[0]和&c[0]地址是一致的:
var b []intvar c []intfunc test(a []int) { c = a[:1] b = append(a[:0:0], a[:1]...) fmt.Println(&a[0], &c[0], &b[0]) }
推薦閱讀
喜歡本文的朋友,歡迎關注「Go語言中文網」:
Go語言中文網啟用微信學習交流群,歡迎加微信:274768166,投稿亦歡迎