Go-实战之 Go Slice 的坑


内存陷阱 1.0

先看下面的代码,看看有没有什么明显的问题?

package main

import "fmt"

func getFirstThreeNumber() []byte {
	res := make([]byte, 1000)
	fmt.Println(len(res), cap(res))
	return res[:3]
}

func main() {
	res := getFirstThreeNumber()
	fmt.Println(len(res), cap(res))
}

看起来很合理啊,切片截取数据返回嘛,木的问题。

但是看下输出结果

1000 1000
3 1000

返回的数据竟然也分配了 1000 的容量,emm 这么看可能没啥,把问题极端化就很严重了。(我尝试分配11G 的切片,然后把我那台 32G 的笔记本给干崩了 = ̄ω ̄=)

假设这个数据需要很大,那么是不是代表,我只存储了一小段数据,但是实际占据了一大段内存,这就是所谓的内存陷阱。

解决方法如下

package main

import "fmt"

func getFirstThreeNumber() []byte {
	src := make([]byte, 1000)
    //  可以作死试试
    //	src := make([]byte, 1e11)
	fmt.Println(len(src), cap(src))
	// 重新申请新的切片,长度为0,容量为3
	// 这样子切片的长度为0,容量为3,不会引用原切片的底层数组,也不会引发内存泄漏
	dst := make([]byte, 0, 3)
	copy(dst, src[:3])
	return dst
}

func main() {
	res := getFirstThreeNumber()
	fmt.Println(len(res), cap(res))
}

内存陷阱2.0

1.0 数组是直接存储数据,2.0 数组是存储指针。

如果没有对指针进行清空,会导致指针对应的数据没有被回收

package main

import (
	"fmt"
	"runtime"
	"time"
)

type data []byte

func WithoutSetNil(slice *[]*data) {
	//去除最后一个
	*slice = (*slice)[:len(*slice)-1]
}

func WithSetNil(slice *[]*data) {
	//设置最后一个为nil
	(*slice)[len(*slice)-1] = nil
	*slice = (*slice)[:len(*slice)-1]
}

// 比较两者的内存占用
func main() {
	var slice1 []*data
	var slice2 []*data

	// 初始化切片
	for i := 0; i < 2; i++ {
		d1 := data(make([]byte, 1e7))
		slice1 = append(slice1, &d1)
		d2 := data(make([]byte, 1e7))
		slice2 = append(slice2, &d2)
	}

	// 记录初始内存使用情况
	runtime.GC()
	time.Sleep(time.Second) // 留出时间给 GC
	var m runtime.MemStats
	runtime.ReadMemStats(&m)
	fmt.Printf("Initial memory usage: %d bytes\n", m.Alloc)

	// 不设置nil
	WithoutSetNil(&slice1)
	runtime.GC()
	time.Sleep(10 * time.Second) // 留出时间给 GC
	runtime.ReadMemStats(&m)
	fmt.Printf("Memory usage after WithoutSetNil: %d bytes\n", m.Alloc)
	
	// 设置nil
	WithSetNil(&slice2)
	runtime.GC()
	time.Sleep(10 * time.Second) // 留出时间给 GC
	runtime.ReadMemStats(&m)
	fmt.Printf("Memory usage after WithSetNil: %d bytes\n", m.Alloc)

	// 这里还要再使用一次slice1, slice2, 否则会被优化掉
	fmt.Println(slice1, slice2)

}
Initial memory usage: 40156272 bytes
Memory usage after WithoutSetNil: 40158120 bytes
Memory usage after WithSetNil: 30155712 bytes
[0xc000008048] [0xc000aa4000]

如果本文帮助到了你,帮我点个广告可以咩(o′┏▽┓`o)


评论
  目录