垃圾回收
一、什么是垃圾回收?
对没用的内存资源进行自动回收
不回收容易引发内存泄露问题
二、垃圾回收常用方法
1.引用计数(reference counting)
原理是在每个对象内部维护一个整数值,叫做这个对象的引用计数,当对象被引用时引用计数加一,当对象不被引用时引用计数减一。当引用计数为 0 时,自动销毁对象。
注意:
- 回收的是内存空间
- 链式引用都要跟着更新计数
缺陷:
- 不能解决循环引用的问题
- 频繁更新引用计数降低了性能
package main
import "fmt"
func main() {
name := "hallen"
var ptrName *string = &name
fmt.Println(&ptrName) //0xc00000602
*ptrName = "" // 引用
fmt.Println(&ptrName) //0xc00000602
}
2.标记-清除(mark & sweep)(golang 1.5以前用的)
标记:从程序的根节点开始, 递归遍历所有对象,将能遍历到的对象打上标记。
清除:将所有未标记的的对象当作垃圾销毁。
缺点:
- 在标记时必须暂停整个程序,递归遍历,会消耗很多时间
3.三色标记算法(Go 1.5、Go 1.6。)也叫三色并发标记法
三色标记法是传统 Mark-Sweep 的一个改进,它是一个并发的 GC 算法。
通过三个阶段确定要清除的对象有哪些
原理:
- 首先创建三个集合:白、灰、黑。
- 新创建(访问过的对象)的对象放入白色集合中。
- 然后从根节点开始遍历所有对象(注意这里并不递归遍历,只遍历一次),把遍历到的对象从白色集合放入灰色集合。
- 之后遍历灰色集合,将灰色对象_引用的对象_从白色集合放入灰色集合,之后将此灰色对象放入黑色集合
- 重复 上面一部,直到灰色中无任何对象
- 通过write-barrier检测对象有变化,重复以上操作,直到灰色对象中没有对象
- 收集所有白色对象(垃圾)
注意:
- 如果没有STW,在标记的时候对象被引用了,会出现对象丢失现象,
缺陷:
- 垃圾产生的速度会大于垃圾收集的速度,这样会导致程序中的垃圾越来越多无法被收集掉。
4.分代收集(Generational Garbage Collection)(java、.Net等用的就是这种)
分代收集也是传统 Mark-Sweep 的一个改进
原理如下:
- 新对象放入第 0 代
- 当内存用量超过一个较小的阈值时,触发 0 代收集
- 第 0 代幸存的对象(未被收集)放入第 1 代
- 只有当内存用量超过一个较高的阈值时,才会触发 1 代收集
- 2 代同理