new操作是我们常用的堆空间分配方法,可以说离了它,我们基本写不出可用的工程代码。可是一切内存问题却皆源于此:
1、内存碎片化:有足够的内存,却申请不到整块的空间;
2、性能低下:new操作是考虑到多线程安全的,因此,调用时增加的多线程安全的负担;在堆空间上分配内存时,进程要进行内核态切换,又增加了额外的开销;
3、内存丢失:不用说,忘记delete的对象一直占据着堆空间,我们的程序想长期稳定运行简直是个梦...
来吧,我们做个测试,探一探这个“万恶之源”的真面目:
//## class CComplex
class CComplex {
Friends
public :
Constructors and destructors
//## operation CComplex(double,double)
CComplex(double a = 0.0, double b = 0.0) : r(a), c(b){}
//## operation ~CComplex()
virtual ~CComplex();
Attributes
protected :
union {
struct {
double r; //## attribute r
double c; //## attribute c
};
CComplex * next;
};
};
static void signal_handler(int signum)
{
cout << "Get a signal " << signum << endl;
DEBUG_BACKTRACE();
exit(1);
}
/*
* === FUNCTION ======================================================================
* Name: main
* Description:
* =====================================================================================
*/
int main ( int argc, char *argv[] )
{
CComplex * _array[50000];
signal(SIGSEGV, signal_handler);
signal(SIGABRT, signal_handler);
DEBUG_TIME_BEGIN();
for(int i = 0; i < 5000; i ++)
{
for(int j = 0; j < 50000; j ++)
{
_array[j] = new CComplex(i, j);
}
for(int j = 0; j < 50000; j ++)
{
delete _array[j];
}
}
DEBUG_TIME_DUR();
DEBUG_TIME_END();
return 0;
} /* ---------- end of function main ---------- */
测试结果本稳定在11.6s左右(Ubuntu10.04 64bit)。
那如果我们提供一份自己的内存管理器后,会有什么不同呢?
#define POOL_SIZE 32
/*
* =====================================================================================
* Class: IMemoryManager
* Description:
* =====================================================================================
*/
class IMemoryManager
{
public:
virtual void * allocate(size_t) = 0;
virtual void release(void *) = 0;
protected:
private:
}; /* ----- end of class IMemoryManager ----- */
/*
* =====================================================================================
* Class: CMemoryManager
* Description:
* =====================================================================================
*/
class CMemoryManager : public IMemoryManager
{
struct FreeStore
{
FreeStore * next;
};
void expandPoolSize()
{
// cout << "Expand Pool Size!" << endl;
size_t size = (sizeof(CComplex) > sizeof(FreeStore *)) ?
sizeof(CComplex) : sizeof(FreeStore *);
FreeStore * head = reinterpret_cast<FreeStore *>(new char[size]);
freeStoreHead = head;
for(int i = 0; i < POOL_SIZE; i ++)
{
head->next = reinterpret_cast<FreeStore *>(new char[size]);
head = head->next;
}
head->next = NULL;
}
void cleanup()
{
// cout << "Clean Up!" << endl;
FreeStore * nextPtr = freeStoreHead;
for(; nextPtr; nextPtr = freeStoreHead)
{
freeStoreHead = freeStoreHead->next;
delete [] nextPtr;
}
}
FreeStore * freeStoreHead;
public:
CMemoryManager () /* constructor */
{
cout << "Create CMemoryManager Object!" << endl;
freeStoreHead = NULL;
expandPoolSize();
}
virtual ~CMemoryManager()
{
cout << "Destory CMemoryManager Object!" << endl;
cleanup();
}
virtual void * allocate(size_t size)
{
if(freeStoreHead == NULL)
expandPoolSize();
FreeStore * head = freeStoreHead;
freeStoreHead = head->next;
return head;
}
virtual void release(void * obj)
{
FreeStore * head = static_cast<FreeStore *>(obj);
head->next = freeStoreHead;
freeStoreHead = head;
}
protected:
private:
}; /* ----- end of class CMemoryManager ----- */
//## class CComplex
class CComplex {
Friends
public :
Constructors and destructors
//## operation CComplex(double,double)
CComplex(double a = 0.0, double b = 0.0) : r(a), c(b){}
//## operation ~CComplex()
virtual ~CComplex();
void *operator new(size_t) throw();
void operator delete(void *);
Attributes
protected :
union {
struct {
double r; //## attribute r
double c; //## attribute c
};
CComplex * next;
};
};
CMemoryManager gMemoryManager;
//## class CComplex
void * CComplex::operator new(size_t size) throw()
{
return gMemoryManager.allocate(size);
}
void CComplex::operator delete(void * obj)
{
gMemoryManager.release(obj);
}
经过测试,在使用了自己内存管理之后,平均的执行时间为7.7s左右,提高了34%。
这个内存管理器存在一个问题是:不支持多线程互斥,因此,在单线程环境中,可以有效提高内存使用的效率(虽然不如参考资料提到的那么多)。
接下来,我将对其进行模板化,使其支持额外的数据类型,并测试其增加互斥后的性能,完整代码会放到空间的代码分类中。
参考资料:http://www.ibm.com/developerworks/cn/education/aix/au-memorymanager/index.html
续:经过测试,增加mutex互斥后,平均执行时间为8.4s,可见去除掉内核态的切换,多线程安全的new的性能仍有很大程度的提升。