天天看點

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*) > ) ? sizeof(void*) : ;
  //驗證一個數是2的指數的奇巧淫技
  assert((align & (align-)) == );   // Pointer size should be a power of 2
  //記憶體對齊的奇巧淫技
  size_t current_mod = reinterpret_cast<uintptr_t>(alloc_ptr_) & (align-);
  size_t slop = (current_mod ==  ?  : 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-)) == );
  return result;
}
           

leveldb向記憶體申請一塊空間的請求交由arena實作,就會調用AllocateAligned函數,配置設定時要求記憶體對齊。

當現有的block剩餘空間不足時,需要重新申請

block(AllocateFallback)
char* Arena::AllocateFallback(size_t bytes) {
  if (bytes > kBlockSize / ) {
    // 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_大小的浪費。