## 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回收。