Go语言在什么情况下会发生内存泄漏

内存泄漏主要由协程阻塞、互斥锁未释放和字符串截取引起。Go中的协程如果无法正常退出会导致内存占用,特别是无缓冲通道可能导致死锁。未释放的互斥锁和持续运行的time.Ticker也会造成内存泄漏。此外,字符串切片可能导致大字符串无法回收。检测内存泄漏可以使用监控工具或Go的pprof工具。
摘要由CSDN通过智能技术生成

什么是内存泄漏?

预期很快能被释放的内存由于某些原因生命周期意外地被延长,导致预计能够立即回收的内存却长时间得不到回收,这就是内存泄漏。

Go语言中内存泄漏的原因

  1. goruntine(协程)导致的内存泄漏(最主要的原因)

go语言中协程的创建非常简单,导致我们很多时候只关心协程的代码功能实现,而忽略了协程什么时候结束退出的问题。如果协程在执行时被阻塞而无法退出,就会导致协程的内存泄漏。一个协程的最少占用为2k的内存,在高并发场景下,如果存在大量的协程内存泄漏,则会导致系统严重的内存泄漏。

func finishReq(timeout time.Duration) r ob{
	ch := make(chan ob)
	// ch := make(chan ob,1)
	go func() {
		result := fn()
		ch <- result
    }()
	select {
	case result = <- ch:
	    return result
	case <- time.After(timeout):
		return nil
    }
}

finishRequest函数在第4行使用匿名函数创建子goroutine来处理请求,这是Go服务器程序中的常见做法。子goroutine执行fn(),并通过第6行的通道ch将结果发送回父goroutine。子协程将在第6行阻塞,直到父级从第9行的ch中提取结果。同时,父级将在select时阻塞,直到子级将结果发送给ch(第9行)或发生超时(第11行)。如果超时提前发生,或者如果Go运行时(非确定性)在两个情况都有效的情况下选择了第11行的情况,则父级将从第12行的requestReq()返回,其他任何人都不能再从ch中提取结果,导致子协程永远被阻止修复方法是将ch从非缓冲通道更改为缓冲通道,这样子goroutine即使在父级退出时也可以始终发送结果。

  1. 互斥锁未被释放,导致内存泄漏
  2. time.Ticker是每间隔指定的时间就会向通道内写数据。作为循环触发器,必须调用stop方法才会停止,从而才能被GC回收,否则将会一直占用内存空间;
  3. 字符串的截取而引发的内存泄漏。一个长的字符串被另一个字符串通过切片的方式截取,这两个字符串共用一个底层数组。如果截取的字符串很小,但是原来的字符串很大,只要截取的小字符串还在活跃,则大的字符串将不会被回收,这样会造成临时的内存泄漏;
  4. 同理切片的截取也会存在这样的情况。

Go发现内存泄漏的两种方法

  1. 一个是通用的监控工具,如果在监控工具中发现随着时间的推进,内存的占用率在不断的提高,这就是内存泄漏最明显的现象。
  2. 另一个是go pprof,pprof是性能分析工具,在程序运行过程中可以记录程序的运行信息,CPU使用情况,内存使用情况、协程运行情况等。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值