内存陷阱 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]