Go語言高級編程(Advanced Go Programming) git
Go語言高級編程(Advanced Go Programming)程序員
golang都是傳值,與其餘語言不一樣的是數組做爲參數時,也是傳值!
可是,lambda閉包引用的外部變量,則是引用!
另外,slice、string雖然也是傳值,但其本質上是一個引用信息(指針、長度等信息),不涉及具體的內容。golang
空數組 [0]int{}、空結構體struct{}{} 本質上都不佔用內存空間,很是好用,固然後者用的更多。編程
空切片 sc[:0] 在特定場合下很是有用!例如字符串的去除空格、過濾等功能。見代碼數組
func RemoveBlank(s string)[]byte{ b := s[:0] for(i := 0; i < len(s); i++){ if s[i] != ' '{ b = append(b, s[i]) //在原內存上操做,且確定不會超出,效率很是高 } } } 閉包
切片底層是數組,若是切片一直存在,那麼數組也不會被釋放。因此這裏可能存在嚴重的內存浪費行爲。
例如從文件內容中查找指定的內容,則可能會發生這種狀況:讀取了整個文件,返回了一個很大的[]byte,但最終返回的是一個很小的[]byte,這時候底層的數組不會被釋放!
這時,最好就是將獲取到的結果append到全新的切片中。app
func FindPhoneNumber(file string)[]byte{ b, _ := ioutil.ReadFile(file) //return regexp.MustCompile(`[0-9]+`).Find(b) //FIXME 不推薦!存在浪費的可能! b = regexp.MustCompile(`[0-9]+`).Find(b) return append([]byte{}, b...) //這樣就OK了 } 函數
另外,還有一種可能,就是切片中存的是指針,當縮小切片的範圍時,範圍外的指針仍然存在!一樣會阻礙GC的進行!線程
//bad demo var a[]*int{ ... } a = a[:len(a)-1] //注意:此時最後一個元素仍然存在,不會被GC! 指針
那須要怎麼作呢? 首先將不須要的元素置爲nil,再切片就OK啦:
//good demo var a[]*int{ ... } a[len(a)-1] = nil // a = a[:len(a)-1]
若是切片存在的週期很短的話,能夠不用刻意處理這個問題!
在main.main函數執行以前全部代碼都運行在同一個goroutine,也就是程序的主系統線程中。
所以,若是某個init函數內部用go關鍵字啓動了新的goroutine的話,新的goroutine只有在進入main.main函數以後纔可能被執行到。
package main import ( "fmt" "time" ) // main.main()執行以前,只有一個main goroutine,所以,哪怕init中有goroutine,也只能等到main.main()執行時才能執行 func init() { fmt.Println("init in") go func() { fmt.Println("init goroutine in") //看看這行信息出現的時間 time.Sleep(time.Second * 5) fmt.Println("init goroutine out") }() fmt.Println("init out") } func main() { fmt.Println("-----main") time.Sleep(time.Second * 10) }
Go語言函數的遞歸調用深度邏輯上沒有限制,函數調用的棧是不會出現溢出錯誤的(相對而言),由於Go語言運行時會根據須要動態地調整函數棧的大小。每一個goroutine剛啓動時只會分配很小的棧(4或8KB,具體依賴實現),根據須要動態調整棧的大小,棧最大能夠達到GB級(依賴具體實現)。
package main import "fmt" //golang 的棧不能超過 1000000000-byte limit func main() { defer func() { if e := recover(); e != nil { fmt.Println(e) } }() n := 500000000 r := factorial(n) fmt.Printf("%v 的階乘是:%v\n", n, r) } //factorial 計算階乘 - 不考慮溢出 func factorial(n int) int { if n == 0 { return 1 } return n * factorial(n-1) }
由於,Go語言函數的棧不會溢出,因此普通Go程序員已經不多須要關心棧的運行機制的。
在Go語言規範中甚至故意沒有講到棧和堆的概念。
咱們沒法知道函數參數或局部變量究竟是保存在棧中仍是堆中,咱們只須要知道它們可以正常工做就能夠了。
由於不須要考慮堆、棧問題,因此徹底能夠這麼寫
func tmp()*int{ x := 10 return &x //這是C/CPP中是徹底不容許的,由於局部變量在棧上,函數執行完畢就會被銷燬 - 可是golang足夠智能,會自動在堆上建立 - 若是你非要關注堆、棧的話。 }
golang足夠智能,會自行判斷。
未完待續