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
}
复制代码