golang中省略返回值形成内存泄漏

我已经两次由于不恰当的省略go中的函数返回值,一次形成MySql的too many connection错误,一次形成严重的内存泄漏。因此在这里你们分享一下这个问题和解决办法,也提醒本身之后不要再犯相似的错了。数组

众所周知,go中的函数能够返回多个值。但不少时候咱们并不须要全部的值,并且go中定义了一个变量必须使用才能够,否则会报错。因此对于不须要的返回值,通常的操做方法就是省略:函数

for _,value := range slice{
	//....	
}

一个典型就是上面的range。range能够返回两个值:若是后面是数组或者切片,第一值就是index索引号。若是range后面是map类型,第一值就是map的键key。第二值就是数据里或者map中具体的值了。不少时候咱们不须要第一个值,因此就像上面代码中写的同样,直接省略就好。这样的处理办法在通常状况下是没有什么问题,但有的状况下就会出现严重的问题。好比下面这段代码:
工具

for {
	_, err = http.Post(url, "", nil)
	if err != nil {
		fmt.Println(err)
	}
	time.Sleep(Interval)
}

由于不须要返回的数据,只要访问不发生错误就行,因此我直接把第一个值省略点了。而后运行,而后就看到任务管理器里面看到,程序进程的内存一直飞增。厉害的时候刷新一次能增长2-3MB!当时就以为有点蒙了,由于在循环中的就这小部分,而这部分用的都是官方的库。当时的念头就是难道是官方库存在内存泄漏,想一想又以为这是不可能的。google

在google上搜了半天,打算用pprof。说实话这个工具我真不会用,只是当时也没办法,无论合适不合适就直接上了。果真分析结果对我来讲就像天书同样。胡乱看看,只是发现goroutine增加的很是快。点进去看看,满屏的参数也看不懂。这个时候我看到bufio这个包,忽然想到之前遇到的一次错误。url

前些时候在使用go调用MySql的时候会出现too many connection的错误。当时的一个缘由就是我省略了一个返回值,因而资源一直没有释放,最后耗尽了MySql的链接数。此次会不会同样?因而我改了下上面的代码:spa

var resp *http.Response
for {
	resp, err = http.Post(url, "", nil)
	resp.Body.Close()
	if err != nil {
		fmt.Println(err)
	}
        time.Sleep(Interval)
}

而后再运行,问题解决了!
指针

对比两次的代码,我发现了问题的所在:除了由于我省略的参数里面有须要释放的资源,还由于两次省略的参数都是指针!这才是关键!指针本质只是一个地址,并非值的自己。因此虽然咱们省略了返回值,也只少建立了一个指针而已。而在咱们调用的函数里面,已经把变量建立好了,该消耗的内存已经消耗掉了。随着不断的循环,没有释放的资源愈来愈多,内存消耗也就愈来愈大了。这就是问题的关键。code

第一次遇到这个问题的时候,简单的觉得虽然我省略了返回值,可是go仍是会建立个匿名变量什么的,会形成内存的泄漏。知道此次再遇到这样的问题,才想明白问题的本质缘由是什么。索引

此次的bug,给个人教训就是,若是go里面一个函数返回的值是指针,必定要当心,不要轻易省略。否则颇有可能形成已经在函数里面申请的内存空间,由于没法释放而不断的积累。而go文档里面强调要手动释放的资源,好比http.Response.Body或者是os.File,也不要轻易的省略(通常也不会省略的……),并且必定要记住释放,使用defer是最靠谱的(不过也有一个坑……)。进程

相关文章
相关标签/搜索