链表方式的内存管理特点是可以管理足够多的对象(对象的数量依赖指针的范围),不足的地方是无法精确的满足小内存块的申请,例如1字节、2字节大小的对象,因为链表节点指针是4字节(32位平台)。
我们可以通过简单的算法避免这种情况,例如宏:
#define ALLOC_ROUNDUP(X, Y) ((X)+((Y)-1)&~((Y)-1)),我们可以采用这个宏调整block_size,使用户的对象大小保持在某个值的倍数,前面的构造函数可以修改为:
DataBlock::DataBlock(size_type block_size)
{
block_size_ = ALLOC_ROUNDUP(block_size_, 4);
base_addr_ = new unsigned char[block_size_ * MAX_BLOCKS];
/*初始化,生成链表*/
NodePtr node = head_node_ = (NodePtr)base_addr_;
head_node_->next_ = end_node();
for (size_type n = 1; n < MAX_BLOCKS; n++) {
node->next_ = (NodePtr)(base_addr_ + n*block_size_);
node = node->next_;
node->next_ = end_node();
}
}
红字部分是增加的代码,它使block_size_调整为4的倍数,这个算法也能避免字节边界的问题。
虽然通过调整block_size_使我们的内存管理器看上去适应性更强,但是如果用户对内存的需求小内存块比较多,那么我们链表方式的内存管理器会浪费更多的内存。下面的小对象内存分配方式是采用《C++设计新思维》里面的方式,在这本书里面作者采用对对象前一个字节进行添加计数的方式进行管理,一个字节最大值255,那么采用这种方式用户对象申请数量最大上限为255,下面代码摘录于《C++设计新思维》供大家参考:
struct Chunk
{
void Init(std::size_t blockSize, unsigned char blocks);
void* Allocate(std::size_t blockSize);
void Deallocate(void* p, std::size_t blockSize);
void Reset(std::size_t blockSize, unsigned char blocks);
void Release();
unsigned char* pData_;
unsigned char
firstAvailableBlock_,
blocksAvailable_;
};
Chunk::Init(std::size_t blockSize, unsigned char blocks)
{
pData_ = new unsigned char[blockSize * blocks];
Reset(blockSize, blocks);
}
Chunk::Reset(std::size_t blockSize, unsigned char blocks)
{
firstAvailableBlock_ = 0;
blocksAvailable_ = blocks;
unsigned char i = 0;
unsigned char* p = pData_;
for (; i != blocks; p += blockSize)
{
*p = ++i;
}
}
在Reset方法里面作者对申请的代码进行分段计数,它的内存分配和回收方法分别为:
Void *Chunk::Allocate(std::size_t blockSize)
{
if (!blocksAvailable_) return 0;
assert((firstAvailableBlock_ * blockSize) / blockSize ==
firstAvailableBlock_);
unsigned char* pResult =
pData_ + (firstAvailableBlock_ * blockSize);
firstAvailableBlock_ = *pResult;
--blocksAvailable_;
return pResult;
}
Void Chunk::Deallocate(void* p, std::size_t blockSize)
{
assert(p >= pData_);
unsigned char* toRelease = static_cast<unsigned char*>(p);
// Alignment check
assert((toRelease - pData_) % blockSize == 0);
*toRelease = firstAvailableBlock_;
firstAvailableBlock_ = static_cast<unsigned char>(
(toRelease - pData_) / blockSize);
// Truncation check
assert(firstAvailableBlock_ == (toRelease - pData_) / blockSize);
++blocksAvailable_;
}
《未完待续......》