1.什么是引用计数法
GC 原本是一种“释放怎么都无法被引用的对象的机制”。那么人们自然而然地就会
想到,可以让所有对象事先记录下“有多少程序引用自己”。让各对象知道自己的“人气指
数”,从而让没有人气的对象自己消失,这就是引用计数法(Reference Counting)
2.引用计数法是怎么工作的
这里用一张图来表示
image.png
开始的时候 根→A→B 此时A被根引用A的的计时器+1B被A引用 B的计数器+1
当mutator发生改变是 根→A→C 此时因为B的引用链断掉 B的计数器-1 C被A引用C的计数器+1
因为B的计数器变为0 把B的内存空间 存放的空闲链表等待分配
3.引用计数法有什么优缺点
优点:(1)可以即刻会后垃圾
(2)最大暂停时间短
缺点:(1)计数器值的增减处理繁重
(2)计数器需要占用很多位
(3)循环引用无法回收
我们改如何优化呢
我们可以使用部分标记清除的方法
部分标记清除方法是如何进行的?
在标记清除法中我们使用4种颜色,来标识当前对象的4中状态
- 黑(BLACK):绝对不是垃圾的对象(对象产生时的初始颜色)
- 白(WHITE):绝对是垃圾的对象
- 灰(GRAY):搜索完毕的对象
-
阴影(HATCH):可能是循环垃圾的对象
虽然这么说 但是我们是没有办法给对象图颜色的而是向头部分配两个字节的控件来标识当前对象的状态
00~11
下面我们解释他是如何进行的
首先我们假设有一个堆 现在是这个样子
image.png
接下来我们删除根到A的引用
此时变成 image.png
由于A的已引用被删除了,那么指向A的指正被添加到 hatch_queue 测试A头部的计数器减一 此时A会被图为灰色 ,这个队列的存在是为了连接那些可能是循环引用的一部分对象。连接到队列的对象会被作为GC标记清除算法的对象,是的循环引用的垃圾可以被回收
分配
当可以分配时,对象就会被涂回黑色当队列不为空时,程序会通过 scan_hatch_queue() 函数搜索队列,分配分块。 scan_hatch_queue() 函数执行完毕后,程序会递归地调用 new_obj() 函数再次尝试分配。
如果队列为空,则分配将会失败
下面开始搜索逐步hatch_queue队列
1.把所有对象置为灰色
2.除了第一个指向的对象所有的子对象的计数器减一
具体进行过程
image.png
(1)A置为灰色 并且B的计数器减一
(2)B置为灰色C的计数器减一
(3)C置为灰色 A和F计数器减一
(4)F置为灰色
3.扫描所有灰色对象 计数器为0择置为白色 是垃圾
计数器不为0择这位黑色不是垃圾
部分标记清除并不是所有的都是用标记清除的这块只对于循环引用的对象是使用,那么我们如何发现有循环引用的对象请群呢
首先我们分析一下如何循环垃圾是如何产生的
1.产生循环引用
2.删除从外部到循环引用的引用
产生过程:如图所示
image.png
在此请注意对象 A的计数器,其计数器值由 2变成了 1。
部分标记 - 清除算法中用 dec_ref_cnt() 函数来检查这个值。如果对象的计数器值减量
后不为 0,说明这个对象可能是循环引用的一份子。这时会先让这个对象连接到队列,以方
便之后搜索它。
即变化后如果计数器不为1 就可能是循环引用的一部分
就是部分标记清除算法去清理一遍
部分标记清除算法的缺点
然而,部分标记 - 清除算法并不是完美的,因为从队列搜索对象所付出的成本太大了。
被队列记录的对象毕竟是候选垃圾,所以要搜索的对象绝对不在少数。这个算法总计需要3.7 部分标记 - 清除算法 65
查找 3 次对象,也就是说需要对从队列取出的阴影对象分别执行 1 次 mark_gray() 函数、
scan_gray() 函数以及 collect_white() 函数。这大大增加了内存管理所花费的时间。
此外,搜索对象还害得引用计数法的一大优点 — 最大暂停时间短荡然无存
网友评论