1. 程式人生 > >leveldb學習:記憶體池Arena

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