Go切片导致内存泄露

参考:Go 切片导致内存泄露,被坑两次了!

var a []int

func f(b []int) []int {
 a = b[:2]
 return a
}

func main() {
    ...
}

上面的代码会发生内存泄漏。

切片的底层结构:

type SliceHeader struct {
 Data uintptr
 Len  int
 Cap  int
}

切片在运行时的表现为SliceHeader结构体,其定义如下:

Data: 执行具体底层数组的指针

Len:表示切片的长度

Cap:表示切片的容量(底层数组的容量)

切片真正存储数据的地方,是一个数组。切片的 Data 属性中存储的是指向所引用的数组指针地址

内存泄露的原因:

在上述案例中,我们有一个包全局变量 a,共有 2 个切片 a 和 b,截取了 b 的一部分赋值给了 a,两者存在着关联。

打开程序底层来看,他应该是如下图所示:

图片

切片 a 和 b 都共享着同一个底层数组(共享内存块),sliceB 包含全部所引用的字符。sliceA 只包含了 [:2],也就是 0 和 1 两个索引位的字符。

图片

泄露的点,就在于虽然切片 b 已经在函数内结束了他的使命了,不再使用了。但切片 a 还在使用,切片 a 和 切片 b 引用的是同一块底层数组(共享内存块)。

切片 a 引用了底层数组中的一段

虽然切片 a 只有底层数组中 0 和 1 两个索引位正在被使用,其余未使用的底层数组空间毫无作用。但由于正在被引用,他们也不会被 GC,因此造成了泄露。

解决方法:

思路:防止出现两个变量同时应用同一个底层数组。

利用切片的特性。当切片的容量空间不足时,会重新申请一个新的底层数组来存储,让两者彻底分开

var a []int
var c []int    // 第三者

func f(b []int) []int {
 a = b[:2]
  
  // 新的切片 append 导致切片扩容
 c = append(c, b[:2]...)
 fmt.Printf("a: %p\nc: %p\nb: %p\n", &a[0], &c[0], &b[0])
  
 return a
}

返回结果:

a: 0xc00001e080
c: 0xc000018080
b: 0xc00001e080

新增一个临时变量c,它容量为0,使用append做追加,发生内存不足进行扩容,生成一个新的底层数组,将原本的切片设置为nil,完成了底层数组的区分。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值