经典GC算法
引用计数
每个对象保存自己被引用的数量。当引用数量为 0 时,将其回收。(可以立刻回收)
缺点
存在循环引用无法释放问题: 多个对象互相引用
延伸
循环引用解决方法:强弱引用
「若 A 强引用了 B,那 B 引用 A 时就需使用弱引用,当判断是否为无用对象时仅考虑强引用计数是否为 0,不关心弱引用计数的数量」
根集合:栈上的引用、全局变量常量、程序的元数据等等
标记-清扫(为解决上面的循环引用问题)【标记工作一次完成】
- 内存到达某个阈值或者固定时间长度会执行垃圾回收程序。
- STW(不然mark和用户程序并发会有问题):用户的程序被停止执行,开始运行GC。
- 算法分两个部分:标记(mark)和清扫(sweep)
标记阶段:从根集合出发深(广)度遍历标记所有可达对象
清扫阶段:将未标记的对象回收
缺点
STW(stop the world):需要GC时用户的程序被停止执行,影响应用性能。
三色标记(GC太久、STW太长 ==> 将GC工作分段执行)【标记工作分段完成】
- 起初所有对象都是白色。
- 根集合对象标记为灰色放入队列
- 从队列取出灰色对象变为黑色,并将其所引用的对象标记为灰色放入队列。(还是需要STW,只是时间变短了)
- 重复上一步,直到队列中没有灰色对象。
- 剩下的白色对象就是从根集合出发遍历不到的,需要回收的。
节点复制
- 将堆内存划分为两半
- 遍历过程:将一半存活的对象复制到另一半(如此反复)
缺点
浪费内存
分代收集(Java:标记-清扫、节点复制)
- 新生代分为s0、s1、E
- 标记E、s0存活数据,将存活数据复制到s1,然后清理E、s0
- 标记E、s1存活数据,将存活数据复制到s0,然后清理E、s1
- 反复2、3步骤
注:将多次标记存活的数据放到老年代(15次)
Golang GC
前提知识:
三色标记
写屏障:将修改对象的操作记录下来
触发条件
- 内存大小阈值, 内存达到上次gc后的2倍
- 达到定时时间 ,2min interval
runtime.GC()
GC过程
1、第一次STW
- 在STW情况下开启写屏障
2、GC开始标记工作
- 三色标记(没有STW的三色标记)
- 没有STW 和用户程序并发执行 当用户修改对象会触发写屏障,将对象标记为灰色,并加入单独的扫描队列中
3、第二次STW
- 标记工作完成
- 关闭写屏障
4、清扫阶段
- GC清扫工作
拓展
屏障技术
内存屏障技术是一种屏障指令,它可以让 CPU 或者编译器在执行内存相关操作时遵循特定的约束,目前多数的现代处理器都会乱序执行指令以最大化性能,但是该技术能够保证内存操作的顺序性,在内存屏障前执行的操作一定会先于内存屏障后执行的操作。