干货满满,还不收藏等啥呢?!
目录
时常发生在资源的操作中,如文件打开未关闭、流式请求不关闭。
循环内部每次需要执行一次defer,你怎么办?
首先来看看一般做法:
- for i := 1; i <= 5; {
- defer fmt.Println(i) // Possible resource leak, 'defer' is called in a 'for' loop
- i++
- }
首先,这里已经有了造成资源泄漏的可能性,如果说循环内部做一些资源操作,如文件操作等,
如果是文件关闭,如果循环次数较大,相当于你循环结束时才会执行这n次close操作。
但是这样真的可以吗,有的小伙伴就说了,很显然每次执行的时候i都不是真实的每次应该执行的值
那给defer里传一下参数不就对了?
- for i := 1; i <= 5; i++{
- defer func(num int) { // Possible resource leak, 'defer' is called in a 'for' loop
- fmt.Println(i)
- }(i)
- }
这样真的可以吗?
显然没有达到我们的循环内部每次需要执行一次defer的需求,并且还有资源泄漏的嫌疑。
那到底怎么办?
- func main() {
- for i := 1; i <= 5; {
- func(){
- defer fmt.Println(i) // 每次循环都会执行一次defer,不会押到循环结束后,避免资源泄漏的情况
- i++
- }()
- }
- }
有close操作,换句话说有结束/关闭资源的操作,但并没有机会执行
如:
- fmt.Println("任务进行中")
- a := 1
- if a > 0 { // 模拟出现错误
- fmt.Println("任务出现错误")
- return
- }
-
- fmt.Println("任务正常结束")
- defer func() {
- fmt.Println("这里进行关闭资源等操作") // 假设这里进行关闭资源等操作
- }()
此时你以为的你以为,关闭资源操作真的会执行吗,不会的,此时就会造成资源泄漏。
顾名思义,由goroutine导致的泄漏,指(单/多个)goroutine长期驻留在内存中,野蛮的吞噬CPU和内存
goroutine的职能实际上已经结束了,但goroutine又迟迟不能退出、或goroutine的数量疯狂的非正常上涨。
不能退出的原因比如引用它的chan一直不close(实际上已完成使命)这就造成了goroutine泄漏;
非正常上涨原因就是对代码未能进行有效控制,没有对goroutine的数量做控制、没有对产生它的调用方做条件限制,虽然它很轻量,但也经不住无限制的上涨。
代码如下:
- fmt.Println("任务进行中")
- ch := make(chan int)
-
- // 这里模拟一个功能:发送三次数据后发送结束
- go func() {
- for i := 1; i <= 3; i++ {
- ch <- i
- fmt.Println("已发送: ", i)
- }
-
- fmt.Println("发送已结束了")
- }()
-
- go func() { // 设为协程A
- for i := range ch { // 这里不断读取ch的数据
- fmt.Println("已接收: ", i)
- }
-
- fmt.Println("这里会很寂寞,因为永不会执行")
- }()
-
- select {}
speed running:
- 任务进行中
- 已发送: 1
- 已接收: 1
- 已接收: 2
- 已发送: 2
- 已发送: 3
- 发送已结束了
- 已接收: 3
- fatal error: all goroutines are asleep - deadlock!
显然死锁了,协程A的职能是接收来自ch的数据,接收完它就结束了,实际上已经接收完3就可以结束,但是它结束不了,因为它不知道对方是否发送完毕、自己要接收到什么时候才是个头?
因此协程A就发生了泄漏。
这个代码也不是很典型,因为死锁了它就结束了,更典型的一个情况如下。
goroutine内部有死循环一直在跑,因为代码bug导致一些条件产生了死循环,这时候该goroutine就永不退出,资源一直无法释放,导致泄漏。
数量暴增的情况。
如你要处理数亿条数据(很大),遍历的、并发的开协程去跑,但偏偏每个数据的处理时长很久,这就有可能导致段时间内产生极多的goroutine,一下子就会挤爆内存
导致资源占用非常大,也就是波峰持续时间较长,导致泄漏。
避免这种情况就要求能较好的把控代码,关键时刻能解除阻塞、信号的及时通知、及时进行介入控制、避免无用的一直执行、白跑等。
内存泄漏指内存野蛮增长且无法达到稳定状态,严重影响系统性能。
典型代码
- fmt.Println("任务进行中")
- ch := make(chan int)
-
- go func() {
- flag := false
- if flag {
- ch <- 0 // 永不会写
- }
- }()
-
- // 这里模拟一个功能:发送三次数据后发送结束
- go func() {
- for {
- select {
- case <-ch:
- default:
- fmt.Println("default")
- }
- }
-
- fmt.Println("我什么时候能结束?")
- }()
-
- select {}
模拟其它case没有机会执行,导致default快速不停空转,CPU一下子就打满了