4、goroutine 泄漏的场景
泄漏的场景有很多,但因channel阻塞导致泄漏的场景是最多的。
- channel 阻塞:
无缓冲 channel 阻塞。写操作因为没有读操作而阻塞。
有缓冲 channel 阻塞。缓冲区满了,写操作阻塞。
期待从channel读数据,结果没有goroutine写。 - select 操作
select里也是channel操作,如果所有case上的操作阻塞,且没有default分支进行处理,goroutine也无法继续执行。 - 互斥锁没有释放,互斥锁死锁
多个协程由于竞争资源或者彼此通信而造成阻塞,不能退出。 - 申请过多的goroutine来不及释放
三、内存泄漏的分类
在Go中内存泄漏分为暂时性内存泄漏和永久性内存泄漏。
1、暂时性内存泄漏
暂时性泄漏,指的是该释放的内存资源没有及时释放,对应的内存资源仍然有机会在更晚些时候被释放,即便如此在内存资源紧张情况下,也会是个问题。这类主要是 string、slice 底层 buffer 的错误共享,导致无用数据对象无法及时释放,或者 defer 函数导致的资源没有及时释放。
- 获取长字符串中的一段导致长字符串未释放
- 获取长slice中的一段导致长slice未释放
s0 = s1[:50],
s0 = s1[len(s1)-30:]
获取长string或者切片中的一段内容,由于新生成的对象和老的string或者切片共用一个内存空间,会导致老的string和切片资源暂时得不到释放,造成短暂的内存泄漏。
字符串——两次拷贝,s0 = string([]byte(s1[:50])),s0 := (" " + s1[:50])[1:]
切片——使用copy函数,copy(s0, s1[len(s1)-30:])
- 获取指针切片slice中的一段
s := []*int{new(int), new(int), new(int), new(int)}
return s[1:3:3]
这种情况下:当函数返回后,只要s还存活,s中的所有元素都不能被释放,即使s中的第一个元素和最后一个元素没有被使用了,也不能被释放。我们可以将不要的元素置为nil就能解决这个问题。
s := []*int{new(int), new(int), new(int), new(int)}
s[0], s[len(s)-1] = nil, nil
return s[1:3:3]
- defer 导致的内存泄漏
在for中存在很多defer去进行资源的释放,那么这么多资源只能在函数结束时才能得到释放。
可是使用匿名函数解决这个问题,每一次循环后启动一个函数,函数结束后资源就释放了。
// 错误
func writeManyFiles(files []File) error {
for \_, file := range files {
f, err := os.Open(file.path)
if err != nil {
return err
}
defer f.Close()
\_, err = f.WriteString(file.content)
if err != nil {
return err
}
err = f.Sync()
if err != nil {
return err
}
}
return nil
}
// 正确
func writeManyFiles(files []File) error {
for \_, file := range files {
if err := func() error {
f, err := os.Open(file.path)
if err != nil {
return err
}
// The close method will be called at
// the end of the current loop step.
defer f.Close()
\_, err = f.WriteString(file.content)
if err != nil {
return err
}
return f.Sync()
}(); err != nil {
return err
}
}
return nil
}
2、永久性内存泄漏
永久性泄漏,指的是在进程后续生命周期内,泄漏的内存都没有机会回收,如 goroutine 内部预期之外的for-loop或者chan select-case导致的无法退出的情况,导致协程栈及引用内存永久泄漏问题。
- goroutine 泄漏导致内存泄漏;
channel 阻塞导致 goroutine 阻塞
select 操作导致 goroutine 阻塞
互斥锁没有释放,互斥锁死锁
申请过多的goroutine来不及释放
- 定时器使用不当,time.Ticker未关闭导致内存泄漏;
time.After在定时器到达时,会自动内回收。time.Ticker 钟摆不使用时,一定要Stop,不然会造成内存泄漏。
- 不正确地使用终结器(Finalizers)导致内存泄漏
三、其他不正当使用内存场景
1、大数组作为参数导致短期内内存激增
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!
加入社区》https://bbs.csdn.net/forums/4304bb5a486d4c3ab8389e65ecb71ac0
…(img-CoCqctIT-1725653958902)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!
加入社区》https://bbs.csdn.net/forums/4304bb5a486d4c3ab8389e65ecb71ac0