golang 内存泄漏总结

您所在的位置:网站首页 golang内存泄漏分析 golang 内存泄漏总结

golang 内存泄漏总结

#golang 内存泄漏总结| 来源: 网络整理| 查看: 265

1.内存泄漏归纳

简单归纳一下,还是“临时性”内存泄露和“永久性”内存泄露:

临时性泄露,指的是该释放的内存资源没有及时释放,对应的内存资源仍然有机会在更晚些时候被释放,即便如此在内存资源紧张情况下,也会是个问题。这类主要是 string、slice 底层 buffer 的错误共享,导致无用数据对象无法及时释放,或者 defer 函数导致的资源没有及时释放。

永久性泄露,指的是在进程后续生命周期内,泄露的内存都没有机会回收,如 goroutine 内部预期之外的for-loop或者chan select-case导致的无法退出的情况,导致协程栈及引用内存永久泄露问题。

1.1.什么是内存泄露

内存泄露指的是程序运行过程中已不再使用的内存,没有被释放掉,导致这些内存无法被使用,直到程序结束这些内存才被释放的问题。

Go虽然有GC来回收不再使用的堆内存,减轻了开发人员对内存的管理负担,但这并不意味着Go程序不再有内存泄露问题。在Go程序中,如果没有Go语言的编程思维,也不遵守良好的编程实践,就可能埋下隐患,造成内存泄露问题。

关于Go的内存泄露有这么一句话:

10次内存泄露,有9次是goroutine泄露。 

这篇文章主要介绍Go程序的goroutine泄露,掌握了如何定位和解决goroutine泄露,就掌握了内存泄露的大部分场景。

1.2.goroutine泄露 1.2.1.什么是goroutine泄露

如果你启动了1个goroutine,但并没有符合预期的退出,直到程序结束,此goroutine才退出,这种情况就是goroutine泄露。

提前思考:什么会导致goroutine无法退出/阻塞?

本质:Goroutine泄露的本质是channel阻塞,无法继续向下执行,导致此goroutine关联的内存都无法释放,进一步造成内存泄露。

1.2.2.goroutine泄露怎么导致内存泄露

每个goroutine占用2KB内存,泄露1百万goroutine至少泄露2KB * 1000000 = 2GB内存,为什么说至少呢?

Goroutine执行过程中还存在一些变量,如果这些变量指向堆内存中的内存,GC会认为这些内存仍在使用,不会对其进行回收,这些内存谁都无法使用,造成了内存泄露

goroutine泄露有2种方式造成内存泄露:

goroutine本身的栈所占用的空间造成内存泄露。 goroutine中的变量所占用的堆内存导致堆内存泄露,这一部分是能通过heap profile体现出来的。 1.2.3.goroutine泄露的发现和定位

利用好go pprof获取goroutine profile文件,然后利用3个命令top、traces、list定位内存泄露的原因。

判断依据:在节点正常运行的情况下,隔一段时间获取goroutine的数量,如果后面获取的那次,某些goroutine比前一次多,如果多获取几次,是持续增长的,就极有可能是goroutine泄露。

1.2.4.goroutine泄露的场景

泄露的场景不仅限于以下两类,但因channel相关的泄露是最多的。

channel的读或者写: 无缓冲channel的阻塞通常是写操作因为没有读而阻塞 有缓冲的channel因为缓冲区满了,写操作阻塞 期待从channel读数据,结果没有goroutine写 select操作,select里也是channel操作,如果所有case上的操作阻塞,goroutine也无法继续执行。 1.2.5.编码goroutine泄露的建议

为避免goroutine泄露造成内存泄露,启动goroutine前要思考清楚:

goroutine如何退出? 是否会有阻塞造成无法退出?如果有,那么这个路径是否会创建大量的goroutine? 2.Go101总结 常见内存泄漏的情况

Go程序可能会在一些情况下造成内存泄漏。go101网站总结了各种内存泄漏的情况:

2.1.获取长字符串中的一段导致长字符串未释放 var s0 string // a package-level variable // A demo purpose function. func f(s1 string) { s0 = s1[:50] // Now, s0 shares the same underlying memory block // with s1. Although s1 is not alive now, but s0 // is still alive, so the memory block they share // couldn't be collected, though there are only 50 // bytes used in the block and all other bytes in // the block become unavailable. }

解决方案

func f(s1 string) { s0 = (" " + s1[:50])[1:] } 2.2. 获取长slice中的一段导致长slice未释放 var s0 []int func g(s1 []int) { // Assume the length of s1 is much larger than 30. s0 = s1[len(s1)-30:] } func demo() { s := createStringWithLengthOnHeap(1


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3