go 内存泄漏_go 内存泄露

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、大数组作为参数导致短期内内存激增

img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!

加入社区》https://bbs.csdn.net/forums/4304bb5a486d4c3ab8389e65ecb71ac0
…(img-CoCqctIT-1725653958902)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!

加入社区》https://bbs.csdn.net/forums/4304bb5a486d4c3ab8389e65ecb71ac0

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值