软件测试之之用户层垃圾回收算法[3] 软件测试方法
关键字:数据库设计 以上编码便是使用GCPtr类管理引用计数的一个初步实现,当然这里只是为了演示一下如何管理引用计数,并不是全部功能的实现。具体应用中还有许多操作符需要重载,将在后面进行介绍。
4. 构造函数和析构函数的说明
可以看到在GCPtr的构造函数中,直接由内存地址定位到存放引用计数的位置,将引用计数加1。
析构函数要做的事情也很简单,就是当一个GCPtr类型的变量释放时,直接由要释放的内存地址(已经存放到m_pAddr中)定位到存放引用计数的位置,将引用计数减1。
这种方法在修改引用计数时效率非常高,与哈希表相比无需查表等操作。
5. 需要修改引用计数的情况
实际上,不仅仅是构造函数和析构函数要修改引用计数,牵涉指针的操作基本上都需要修改引用计数,主要有以下三种情况。
1) 当将新地址赋给GCPtr时,需要将原来地址的引用计数减1,新的地址的引用计数加1,这时需要重载运算符“=”号。
2) 当将一个GCPtr赋给另外一个GCPtr时,需要将等号左边的GCPtr指向的内存块的引用计数减1,等号右边的GCPtr指向的内存块的引用计数加1。
3) 当需要对象的副本时,需要调用GCPtr的拷贝构造函数,如将对象当作参数传递给函数,作为函数的返回值等情况都会调用拷贝构造函数。由于复制的对象是指向相同的地址,而复制对象在释放时要调用析构函数,析构函数将引用计数减1,因此需要在拷贝函数里加1以维持引用计数的平衡。
下面给出这几个操作的编码,由于构造函数和析构函数等在前面已经给出,所以这里省略。
template <class T> class GCPtr {
public:
T *m_pAddr; /* 用来记住定义的指针地址便于析构函数使用 */
public:
/* 拷贝构造函数 */
GCPtr(const GCPtr &gcPtr)
{
INT *p = (INT *)gcPtr.m_pAddr-1;
*p += 1;
m_pAddr = gcPtr.m_pAddr;
}
T *operator=(T *t)
{
/* 将原来指向的内存引用计数减1 */
INT *p = (INT *)m_pAddr-1;
*p-= 1;
m_pAddr = t;
/* 将新指向的内存的引用计数加1 */
p = (INT *)t-1;
*p += 1;
return t;
};
GCPtr & operator = (GCPtr &r)
{
/* 将原来指向的内存引用计数减1 */
INT *p = (INT *)m_pAddr-1;
*p-= 1;
m_pAddr = r.m_pAddr;
/* 将新指向的内存的引用计数加1 */
p = (INT *)r.m_pAddr-1;
*p += 1;
return r;
};
};
6. 垃圾收集功能的实现
引用计数的功能实现后,接着要做的就是实现垃圾内存回收,也就是将引用计数为0的内存全部释放后,如何去访问已经分配的内存?必须用一张表来保存所有已分配的内存地址,可以在GC_Malloc()函数分配内存时就将分配的内存地址保存到哈希表里,然后就可以通过对哈希表的遍历来实现对内存垃圾的回收,由于牵涉哈希表的操作,除了修改GC_Malloc()函数外,还需要增加一个初始化函数,在初始化函数里创建哈希表,以下便是实现编码。