引用计数的算法
- 计数器:对象的被引用数(多少程序引用过这个对象)
- 计数器值增减
引用计数法中没有明确启动GC的语句。在mutator的处理过程中通过增减计数器的值来进行内存管理。
涉及计数器增减的情况 - new_obj()和update_ptr()
- 创建对象
引用计数法中,除了free_list中的对象,其余都是活动对象。如果pick_chunk()失败,堆中就没有合适的分块了;如果成功,将该对象计数器置1,说明对象已生成且被引用。
- 指针更新
1. reclaim()将obj链接到空闲链表
2. 先调用inc再调用dec,以处理更新前后指针指向的是同一对象的情况。如果先调用dec,可能导致对象被引用数为0,继续调用inc时所需对象已被回收。
- 引用计数法特征:mutator和内存管理同时运行。
优点
- 可即刻回收垃圾
内存不会被垃圾占用,其他GC算法中直到GC开始执行之前,都有一部分内存被垃圾占用(分块用尽后才会执行GC)。
- 最大暂停时间短
- 不必沿指针查找
相对适用于分布式环境。
一种做法是各个计算节点内回收垃圾使用GC标记-清除算法,考虑节点间引用关系用引用计数法。
缺点
- 计数器增减处理繁重
指针频繁更新导致计数器频繁更新。
- 计数器占位很多
用于引用计数的计数器最大必须能数完堆中所有对象的引用数。
- 实现复杂
- 循环引用无法回收
两个或两个以上对象互相循环引用,使计数器都无法清零,即使该对象组都成了垃圾,也无法被程序回收。
延迟引用计数法
- 是什么
使从根引用的指针变化不反映在计数器。
使用ZCT(Zero Count Table),记录调用dec之后计数器值为0的对象(可能是垃圾,也可能不是)。
- 修改dec函数和内存分配函数
- scan_zct()
zct满的时候调用,内存没有合适的分块用于分配时调用。
scan_zct(){
for (r : $root)
(*r).ref_cnt++
for(obj : $zct)
if (obj.ref_cnt == 0)
remove($zct, obj) //remove obj from zct
delete(obj) //1. 对子对象计数器做--;2. 回收
for (r : $root)
(*r).ref_cnt--
- 优点
减轻指针引用频繁修改导致的计数器增减所带来的负担。
- 缺点
不支持立刻回收垃圾
scan_zct()导致最大暂停时间变长
Sticky引用计数法
1位引用计数法
部分标记-清除算法