关于引用计数GC的实现(Edited)

在前面,谈到了关于GC的一些东西。网上关于GC的东西比较多,现在想想简单GC实现的问题。引用计数法是一个比较简单的想法。

引用计数,主要摘要是:
  是唯一一种没有使用根集的垃圾回收算法,该算法使用引用计数器来区分存活对象和不再使用的对象。一般来说,堆中的每个对象对应一个引用计数器。当每一次创建一个对象并赋给一个变量时,引用计数器置为1。当对象被赋给任意变量时,引用计数器每次加1。当对象出了作用域后(该对象丢弃不再使用),引用计数器减1,一旦引用计数器为0,对象就满足了垃圾收集的条件。
  基于引用计数器的垃圾收集器运行较快,不会长时间中断程序执行,适宜必须实时运行的程序。但引用计数器增加了程序执行的开销,因为每次对象赋给新的变量,计数器加1,而每次现有对象出了作用域,计数器减1。

方法上,有两个重点:
  1.在“幕后”操作地址分配、提供指针操作;
  2.管理一个或多个列表,以维护在程序运行时分配的对象内存。
在实现上,有三个重点:
  1.提供“幕后”操作的封装;
  2.所维护的对象分配列表的信息;
  3.便于进行查找的迭代器。

先看看方法,简单的讲就是:
  通过对每个作用域的每种对象建立一个列表,以维护动态分配的每种对象的信息。该信息包含了该对象的地址,以及该地址被多少指针所引用(引用计数)。当一个新对象被成功分配了内存以后,同时就会在该列表中注册,并设引用计数为1;每当有其它指针指向该对象时,列表中更新该对象地址的引用计数加1.一个简单的策略是,进行垃圾回收的时间设在当对象操出作用范围。需要调用析构函数时,该对象地址引用计数减1;如果对象地址的引用计数减为0,则表示可以进行回收了。

在实现上:
  C++中,通过重载操作符,以封装关于对象内存的分配,对象指针的操作。达到的效果是在语法上基本上是完全按C++语法编写程序。将需要保存在“垃圾列表”中的信息封装成一个类,以对象实例形式放于list中,便于管理。提供一个迭代器——当然,这不是必须的,以方便遍历维护的列表。

在这里综合一些资料, 提供一个代码的简单示例:
一、类简介:
//-----------class GCListInfo-------
template <class T> //这是“垃圾列表”中封装信息的类
class GCListInfo
{
public:
unsigned refcount; // 当前引用计数
T *memPtr; // 指向分配的空间的指针
bool isArray; // 如果指向一个数组,则设为true.
unsigned arraySize; // 如果指向一个数指,此为数组大小.
GCListInfo(T *mPtr, unsigned size=0) ;
};
//----------- class Iter -------
template <class T> //迭代器
class Iter
{
T *ptr; // 当前指针值
T *end; // 指向最后一元素的下一位置
T *begin; // 指向所分配空间的开始
unsigned length; // 序列长度
public:

 ......... //重载及提供STL兼容的运算子

};

//----------- class GCPtr -------
template <class T, int size=0> //封装操作的类
class GCPtr
{
static list<GCListInfo<T> > gcList; // gcList 维护一个垃圾收集list.
T *addr; //指向 GCPtr 指针当前指向的分配的空间
bool isArray; // 如果指向一个数组,则设为true.
unsigned arraySize; // 如果指向一个数指,此为数组大小.
static bool first; // 当第一个 GCPtr 被创建时即设为true.
//返回一个gcList指针信息的iterator
typename list<GCListInfo<T> >::iterator findPtrInfo(T *ptr);
public:
// 为 GCPtr<T> 定义一个迭代器名字.
typedef Iter<T> GCiterator;
// 一个显示 gcList 的工具函数.
static void showList();
// 当程序退出的时候,清除gcList内容。
static void shutdown();
// 构造函数
GCPtr(T *t=NULL) ;
// 拷贝构造函数.
GCPtr(const GCPtr &ob) ;
// Destructor.
~GCPtr();
//回收垃圾。如果至少有一个对象被释放则返回true.
static bool doCollection();
// 重载 指针到 GCPtr 的赋值运算符.
T *operator=(T *t);
// 重载 GCPtr 与 GCPtr 的赋值运算符.
GCPtr &operator=(GCPtr &rv);
//返回一个由当前GCPtr指向的对象引用
T &operator*() ;
//返回当前指向的地址
T *operator->() ;
//重载 [] .返回索引为i的对象引用。
T &operator[](int i) ;
//返回已分配的内存的开始地址的Iter.
typename GCiterator begin() ;
//返回一个Iter指向所分配的空间最后元素的下一个位置.
typename GCiterator end() ;
//得到 gcList 的大小
static int gcListSize() ;
};

二、实现要点:
  容易注意到,GCPtr<T>类是我们最关心的,该类直接与用户打交道。关于在上面提到的维护列表,以及垃圾空间的释放等,主要通过该类的构造函数、析构函数以及静态成员函数doCollection()来完成。下面分别讲讲这三个函数。
1.构建列表:
//构造函数
template <class T, int size>
GCPtr(T *t=NULL)
{
// 将 shutdown() 注册为退出函数 (exit function).
if(GCPtr<T, size>::first){
::atexit(shutdown);
}
GCPtr<T, size>::first = false;
list<GCListInfo<T> >::iterator p;
p = this->findPtrInfo(t);
if(p != GCPtr<T, size>::gcList.end()) //如果 t 已经存在于 gcList中
p->refcount++; // 增加 ref 计数。
else{ //重新创建
GCListInfo<T> gcObj(t, size);
GCPtr<T, size>::gcList.push_front(gcObj);
}
this->addr = t; //得到当前的地址
this->arraySize = size; //得到array大小
if(size > 0)
this->isArray = true;
else
this->isArray = false;
}
  这个比较简单,当建立一个GCPtr对象时,开始判断是否需要在gcList中新建记录,然后是关于该对象所指目标对象的地址的信息。

2.进行垃圾释放:
//GCPtr析构函数.
template <class T, int size>
GCPtr<T, size>::~GCPtr()
{
list<GCListInfo<T> >::iterator p;
p = this->findPtrInfo(this->addr);
if(p->refcount)
p->refcount--; // decrement ref count
//当指针离开作用域时,进行垃圾回收。(这是一个简单的策略)
GCPtr<T, size>::doCollection();

}
  这个原本是该(执行时机策略)最复杂的地方,被简化成这样(-_- *)。也就是说,当成员操出作用范围,需要进行析构时,就开始进行垃圾回收。

3.垃圾回收操作:
//回收垃圾。如果至少有一个对象被释放则返回true.
template <class T, int size>
bool GCPtr<T, size>::doCollection()
{
bool memfreed = false; //标识是否有对象被释放

list<GCListInfo<T> >::iterator p;
do {
// 在 gcList 中查找没有指向的指针。
for(p = GCPtr<T, size>::gcList.begin(); /
p != GCPtr<T, size>::gcList.end(); p++){
if(p->refcount > 0) //有用的指针
continue;

// 如果GCPtr不为null则释放之.
if(p->memPtr){
if(p->isArray){//若指向数组
delete[] p->memPtr;
}
else {
delete p->memPtr;
}
}

// 从 gcList 中删除无用的指针.
GCPtr<T, size>::gcList.remove(*p);
memfreed = true;

// 重新查找
break;
}

} while(p != GCPtr<T, size>::gcList.end());

return memfreed;
}
  这就是在析构函数最后调用的垃圾回收函数。分析一下,相当简单:反复遍历gcList,找到引用计数为0的空间,进行释放就OK。注意到在构造函数注册了showdown为关闭函数,其实也就是在判断如果在程序退出时gcList仍然不为空,则直接调用该函数,以释放空间。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值