这篇“golang内存泄漏的原因是什么”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Golang内存泄漏的原因是什么”文章吧
这篇“golang内存泄漏的原因是什么”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Golang内存泄漏的原因是什么”文章吧。
泄漏原因有:1、time.After()的使用,每次time.After(duration x)会产生NewTimer(),在duration x到期之前,新创建的timer不会被GC,到期之后才会GC;2、time.NewTicker资源未及时释放;3、select阻塞;4、channel阻塞;5、申请过多的goroutine、goroutine阻塞;6、slice引起的等。
默认的time.After()是会有内存泄露问题的,因为每次time.After(duration x)会产生NewTimer(),在duration x到期之前,新创建的timer不会被GC,到期之后才会GC。
随着时间推移,尤其是duration x很大的话,会产生内存泄露的问题,应特别注意
for true {select {case <-time.After(time.Minute * 3): // do something default:time.Sleep(time.Duration(1) * time.Second)}}
为了保险起见,使用NewTimer()或者NewTicker()代替的方式主动释放资源
timer := time.NewTicker(time.Duration(2) * time.Second)defer timer.Stop()for true {select {case <-timer.C:// do somethingdefault:time.Sleep(time.Duration(1) * time.Second)}}
在使用time.NewTicker时需要手动调用Stop()方法释放资源,否则将会造成永久性的内存泄漏
timer := time.NewTicker(time.Duration(2) * time.Second)// defer timer.Stop()for true {select {case <-timer.C:// do somethingdefault:time.Sleep(time.Duration(1) * time.Second)}}
使用select时如果有case没有覆盖完全的情况且没有default分支进行处理,最终会导致内存泄漏
func main() { ch2 := make(chan int) ch3 := make(chan int) ch4 := make(chan int) go Getdata("https://www.baidu.com",ch2) go Getdata("Https://www.baidu.com",ch3) go Getdata("https://www.baidu.com",ch4) select{ case v:=<- ch2: fmt.Println(v) case v:=<- ch3: fmt.Println(v) }}
上述这种情况会阻塞在ch4的消费处导致内存泄漏
func main() {fmt.Println("main start")msgList := make(chan int, 100)go func() {for {select {case <-msgList:default: }}}()c := make(chan os.Signal, 1)signal.Notify(c, os.Interrupt, os.Kill)s := <-cfmt.Println("main exit.get signal:", s)}
上述for循环条件一旦命中default则会出现循环空转的情况,并最终导致CPU暴涨
channel阻塞主要分为写阻塞和读阻塞两种情况
空channel
func channelTest() { //声明未初始化的channel读写都会阻塞 var c chan int //向channel中写数据 go func() { c <- 1 fmt.Println("g1 send succeed") time.Sleep(1 * time.Second) }() //从channel中读数据 go func() { <-c fmt.Println("g2 receive succeed") time.Sleep(1 * time.Second) }() time.Sleep(10 * time.Second)}
写阻塞
无缓冲channel的阻塞通常是写操作因为没有读而阻塞
func channelTest() { var c = make(chan int) //10个协程向channel中写数据 for i := 0; i < 10; i++ { go func() { <- c fmt.Println("g1 receive succeed") time.Sleep(1 * time.Second) }() } //1个协程丛channel读数据 go func() { c <- 1 fmt.Println("g2 send succeed") time.Sleep(1 * time.Second) }() //会有写的9个协程阻塞得不到释放 time.Sleep(10 * time.Second)}
有缓冲的channel因为缓冲区满了,写操作阻塞
func channelTest() { var c = make(chan int, 8) //10个协程向channel中写数据 for i := 0; i < 10; i++ { go func() { <- c fmt.Println("g1 receive succeed") time.Sleep(1 * time.Second) }() } //1个协程丛channel读数据 go func() { c <- 1 fmt.Println("g2 send succeed") time.Sleep(1 * time.Second) }() //会有写的几个协程阻塞写不进去 time.Sleep(10 * time.Second)}
读阻塞
期待从channel读数据,结果没有goroutine往进写数据
func channelTest() { var c = make(chan int) //1个协程向channel中写数据 go func() { <- c fmt.Println("g1 receive succeed") time.Sleep(1 * time.Second) }() //10个协程丛channel读数据 for i := 0; i < 10; i++ { go func() { c <- 1 fmt.Println("g2 send succeed") time.Sleep(1 * time.Second) }() } //会有读的9个协程阻塞得不到释放 time.Sleep(10 * time.Second)}
例如在for循环中申请过多的goroutine来不及释放导致内存泄漏
I/O连接未设置超时时间,导致goroutine一直在等待,代码会一直阻塞。
goroutine无法获取到锁资源,导致goroutine阻塞
//协程拿到锁未释放,其他协程获取锁会阻塞func mutexTest() { mutex := sync.Mutex{} for i := 0; i < 10; i++ { go func() { mutex.Lock() fmt.Printf("%d goroutine get mutex", i) //模拟实际开发中的操作耗时 time.Sleep(100 * time.Millisecond) }() } time.Sleep(10 * time.Second)}
当程序死锁时其他goroutine也会阻塞
func mutexTest() { m1, m2 := sync.Mutex{}, sync.RWMutex{} //g1得到锁1去获取锁2 go func() { m1.Lock() fmt.Println("g1 get m1") time.Sleep(1 * time.Second) m2.Lock() fmt.Println("g1 get m2") }() //g2得到锁2去获取锁1 go func() { m2.Lock() fmt.Println("g2 get m2") time.Sleep(1 * time.Second) m1.Lock() fmt.Println("g2 get m1") }() //其余协程获取锁都会失败 go func() { m1.Lock() fmt.Println("g3 get m1") }() time.Sleep(10 * time.Second)}
waitgroup的Add、Done和wait数量不匹配会导致wait一直在等待
当两个slice 共享地址,其中一个为全局变量,另一个也无法被GC;
append slice 后一直使用,没有进行清理。
var a []int func test(b []int) { a = b[:3] return}
由于数组时Golang的基本数据类型,每个数组占用不通的内存空间,生命周期互不干扰,很难出现内存泄漏的情况,但是数组作为形参传输时,遵循的时值拷贝,如果函数被多个goroutine调用且数组过大时,则会导致内存使用激增。
//统计nums中target出现的次数func countTarget(nums [1000000]int, target int) int { num := 0 for i := 0; i < len(nums) && nums[i] == target; i++ { num++ } return num}
因此对于大数组放在形参场景下通常使用切片或者指针进行传递,避免短时间的内存使用激增。
以上就是关于“golang内存泄漏的原因是什么”这篇文章的内容,相信大家都有了一定的了解,希望小编分享的内容对大家有帮助,若想了解更多相关的知识内容,请关注编程网GO频道。
--结束END--
本文标题: golang内存泄漏的原因是什么
本文链接: https://www.lsjlt.com/news/348393.html(转载时请注明来源链接)
有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341
下载Word文档到电脑,方便收藏和打印~
2024-04-05
2024-04-05
2024-04-05
2024-04-05
2024-04-05
2024-04-05
2024-04-05
2024-04-05
回答
回答
回答
回答
回答
回答
回答
回答
回答
回答
0