概述
增量式垃圾回收
也并不是一个新的回收算法, 而是结合之前算法的一种新的思路.
之前说的各种垃圾回收, 都需要暂停程序, 执行GC, 这就导致在GC执行期间, 程序得不到执行. 因此出现了增量式垃圾回收
, 它并不会等GC执行完, 才将控制权交回程序, 而是一步一步执行, 跑一点, 再跑一点, 逐步完成垃圾回收, 在程序运行中穿插进行. 极大地降低了GC的最大暂停时间.
实现
增量式垃圾回收
只是提出了这样的一个概念, 并没有限定如何去实现. 想必也有不同的实现思路吧.
三色标记算法
此算法将对象做不同的标记
- 白色: 未搜索过的对象
- 灰色: 正在搜索的对象
- 黑色: 搜索完的对象
这里的颜色只是一种虚构的概念, 就是在对象上打tag
.
在GC开始执行时, 所有对象都是白色的, 然后将根集合的对象放到栈中, 并标记为灰色, 依次处理. 将对象从栈中取出, 递归搜索所有子对象, 并标记为灰色, 当子对象搜索完后, 就将对象标记为黑色. 这样, 当一个对象搜索完后, 该对象及其关联的所有子对象就都是黑色的了. 当标记阶段结束后, 所有活动对象都是黑色, 垃圾对象则是白色.
此算法就是通过这样, 逐步对对象进行标记.
三色标记应用于标记清除中
标记清除算法
在标记阶段, 应用三色标记
逐步标记, 每次搜索一定次数后, 就返回执行, 等待下次继续标记, 将标记分为小段穿插在程序中运行.
在清除阶段, 也可以设置一个次数, 每遍历一定数量的对象, 就返回等待下次继续.
三色标记
不光可以应用于标记清除
中, 也可以应用于其他标记算法中.
问题
你以为这就完了么? 天真, 想象一下这样的场景:
// 假设 c, d 是两个对象
$b->son = $d;
$b->son = $c;
// 在这个时候, 开始GC, 将d标记为白色, 将c标记为黑色. 返回
$b->son = $d;
// GC清除阶段, 将c对象保留, 将d对象回收
这样就出现问题了, 也就是说如果我已经对其进行过标记了, 但它在我标记之后进行了修改, 就会导致清除阶段的对象很可能不是当时的真实情况.
那么如何防止这种遗漏的标记呢? 简单粗暴一点, 每次更新指针的时候, 如果对象是白色的, 就将其涂成灰色, 放到待搜索的栈中, 之后重新对其进行标记. 这样就可以保证不会回收到引用的对象, 虽然可能会有一些遗漏对象没有回收, 但是 who care? 下一次再回收咯.
也有不同的写入屏障
处理方法, 在更新对象时进行不同操作.
大概如此….