我已經兩次由於不恰當的省略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是最靠譜的(不過也有一個坑……)。進程