leveldb学习:内存池Arena

和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_大小的浪费。

你可能感兴趣的:(leveldb,内存池,内存对齐,arena)