和SGI版的STL一样,leveldb内存分配也采用了memory pool的整理方式,减少内存不断分配释放过程中造成的空间零碎化和浪费。leveldb的内存池实现可参见arena.h和arena.cc,有关内存池的测试代码有arena_test.cc。arena内存池是leveldb的关键组件,是很多其他功能模块(class)的成员,在cache、memtable、table组件中均有使用。
先看arena的成员变量:
private:
// Allocation state
//当前内存池的池顶
char* alloc_ptr_;
// 当前block还剩的可分配空间
size_t alloc_bytes_remaining_;
// Array of new[] allocated memory blocks
//每块block地址
std::vector<char*> blocks_;
// Bytes of memory in blocks allocated so far
//内存池大小
size_t blocks_memory_;
再看接口:
arena是按block管理内存的,当上层的组件向内存申请内存时,底层的arena将指定早已分配好的block返回给上层,当block剩余的空间不足一次申请所需的空间时,arena重新申请一个block。
char* Arena::AllocateAligned(size_t bytes) {
//将对齐的值设为指针大小和8的小者
const int align = (sizeof(void*) > 8) ? sizeof(void*) : 8;
//验证一个数是2的指数的奇巧淫技
assert((align & (align-1)) == 0); // Pointer size should be a power of 2
//内存对齐的奇巧淫技
size_t current_mod = reinterpret_cast<uintptr_t>(alloc_ptr_) & (align-1);
size_t slop = (current_mod == 0 ? 0 : align - current_mod);
size_t needed = bytes + slop;
char* result;
//当前block剩余空间足够,直接分配,并更新alloc_bytes_remaining_
if (needed <= alloc_bytes_remaining_) {
result = alloc_ptr_ + slop;
alloc_ptr_ += needed;
alloc_bytes_remaining_ -= needed;
} else {
// AllocateFallback always returned aligned memory
//剩余空间不足,重新分配block
result = AllocateFallback(bytes);
}
assert((reinterpret_cast<uintptr_t>(result) & (align-1)) == 0);
return result;
}
leveldb向内存申请一块空间的请求交由arena实现,就会调用AllocateAligned函数,分配时要求内存对齐。
当现有的block剩余空间不足时,需要重新申请
block(AllocateFallback)
char* Arena::AllocateFallback(size_t bytes) {
if (bytes > kBlockSize / 4) {
// Object is more than a quarter of our block size. Allocate it separately
// to avoid wasting too much space in leftover bytes.
char* result = AllocateNewBlock(bytes);
return result;
}
// We waste the remaining space in the current block.
alloc_ptr_ = AllocateNewBlock(kBlockSize);
alloc_bytes_remaining_ = kBlockSize;
char* result = alloc_ptr_;
alloc_ptr_ += bytes;
alloc_bytes_remaining_ -= bytes;
return result;
}
kBlockSize=4096,如果申请的大小大于kBlockSize/4,则将申请一个bytes大小的block,否则,申请一个kBlockSize大小的block,bytes只占block的一部分,剩下的空间交由后面使用。
arena析构函数会把容器block_中指向blocks空间的指针依次delete。也就是释放了内存空间。
缺点:arena在申请的空间大于当前block所剩空间时(needed >= alloc_bytes_remaining_),侧抛弃当前block,重新申请新的一块block,这样就会造成老block的alloc_bytes_remaining_大小的浪费。