## 1. 閉包
閉包可以理解成“定義在一個函數內部的函數“。在本質上,閉包是將函數內部和函數外部連接起來的橋梁。內部函數被保存到外部時,將會生成閉包。閉包會導致原有作用域鏈不釋放,造成內存泄漏。
閉包=函數+引用環境。關鍵詞:自由變量,匿名函數
```
func main(){
resule := getNum()
fmt.Println(resule())
}
func getNum() func() int {
i:=0
return func ()int {
i+=1
return i
}
}
```
從上邊代碼來看當我們 在main函數中調用`result := getNum()`的時候 ,獲取到result 其實是方法getNum () 的返回值,也是一個方法。
這個時候`result := getNum ();`等價于
```
result := func()int {
i:=0
return func()(int){//返回值是一個 int類型
return i
}
}
```
這個時候 result 其實只是一個方法,如果我們打印`fmt.pringln(result)`的話得到的是內存地址 并不是我們想要的結果 ;所以,想要獲取 文檔中i 的 值 我們還需要這樣寫
```
result := getNum()
fmt.println(result())
```
## 2. 回調函數
回調函數就是一個通過函數指針調用的函數。如果你把函數的指針(地址)作為參數傳遞給另一個函數,當這個指針被用來調用其所指向的函數時,我們就說這是回調函數。回調函數不是由該函數的實現方直接調用,而是在特定的事件或條件發生時由另外的一方調用的,用于對該事件或條件進行響應。
#### 使用方法
(1)定義一個回調函數
(2)提供函數實現的一方在初始化的時候,將回調函數的函數指針注冊給調用者
(3)當特定的事件或條件發生時,調用者使用函數指針調用回調函數對事件進行處理
```
type Callback func(x, y int) int
// 提供一個接口,讓外部去實現
func test(x, y int, callback Callback) int {
return callback(x, y)
}
// 回調函數的具體實現
func add(x, y int) int {
return x + y
}
// 調用函數test時,調用真正的實現函數add
func main() {
x, y := 1, 2
fmt.Println(test(x, y, add))
}
```
## 3. 匿名函數
匿名函數是指不需要定義函數名的一種函數實現方式。它由一個不帶函數名的函數聲明和函數體組成
```
func(a,b int,z float64) bool{
return a*b
}
```
匿名函數只是作為一個表達式,那是非常沒有意義的。匿名函數可以直接賦給一個變量,作為結構字段,或者把它傳遞到另外的函數當中或者直接執行。
```
//function variable
fn := func(){
fmt.Println("hello")
}
fmt.Printf("%T\n",fn) //打印func()
```
## 4. 內存泄漏和內存溢出
#### (1)內存泄漏(memory leak)
內存泄漏是指程序中已動態分配的堆內存由于某種原因未釋放或無法釋放,造成系統內存的浪費,導致程序運行速度減慢甚至系統奔潰等嚴重后果。
* 發生方式:?
1\. 常發性內存泄漏。發生內存泄漏的代碼會被多次執行到,每次被執行的時候都會導致一塊內存泄漏。?
2\. 偶發性內存泄漏。發生內存泄漏的代碼只有在某些特定環境或操作過程下才會發生。常發性和偶發性是相對的。對于特定的環境,偶發性的也許就變成了常發性的。所以測試環境和測試方法對檢測內存泄漏至關重要。?
3\. 一次性內存泄漏。發生內存泄漏的代碼只會被執行一次,或者由于算法上的缺陷,導致總會有一塊僅且一塊內存發生泄漏。比如,在類的構造函數中分配內存,在析構函數中卻沒有釋放該內存,所以內存泄漏只會發生一次。?
4\. 隱式內存泄漏。程序在運行過程中不停的分配內存,但是直到結束的時候才釋放內存。嚴格的說這里并沒有發生內存泄漏,因為最終程序釋放了所有申請的內存。但是對于一個服務器程序,需要運行幾天,幾周甚至幾個月,不及時釋放內存也可能導致最終耗盡系統的所有內存。所以,我們稱這類內存泄漏為隱式內存泄漏。
#### (2)內存溢出
程序在申請內存時,沒有足夠的內存供申請者使用,或者說,給了你一塊存儲int類型數據的存儲空間,但是你卻存儲long類型的數據,就會導致內存不夠用,報錯OOM,即出現內存溢出的錯誤。
* 內存溢出原因:?
1.內存中加載的數據量過于龐大,如一次從數據庫取出過多數據;?
2.集合類中有對對象的引用,使用完后未清空,使得JVM不能回收;?
3.代碼中存在死循環或循環產生過多重復的對象實體;?
4.使用的第三方軟件中的BUG;?
5.啟動參數內存值設定的過小。
* 解決方案:
1.修改JVM啟動參數,直接增加內存。(-Xms,-Xmx參數一定不要忘記加。)
2.檢查錯誤日志,查看“OutOfMemory”錯誤前是否有其 它異常或錯誤。
3.對代碼進行走查和分析,找出可能發生內存溢出的位置。
* 問題排查:
1.檢查對數據庫查詢中,是否有一次獲得全部數據的查詢。一般來說,如果一次取十萬條記錄到內存,就可能引起內存溢出。這個問題比較隱蔽,在上線前,數據庫中數據較少,不容易出問題,上線后,數據庫中數據多了,一次查詢就有可能引起內存溢出。因此對于數據庫查詢盡量采用分頁的方式查詢。
2.檢查代碼中是否有死循環或遞歸調用。
3.檢查是否有大循環重復產生新對象實體。
4.檢查對數據庫查詢中,是否有一次獲得全部數據的查詢。一般來說,如果一次取十萬條記錄到內存,就可能引起內存溢出。這個問題比較隱蔽,在上線前,數據庫中數據較少,不容易出問題,上線后,數據庫中數據多了,一次查詢就有可能引起內存溢出。因此對于數據庫查詢盡量采用分頁的方式查詢。
5.檢查List、MAP等集合對象是否有使用完后,未清除的問題。List、MAP等集合對象會始終存有對對象的引用,使得這些對象不能被GC回收。