leveldb分析——Arena內存管理

leveldb中實現了一個簡單的內存管理工具Arena,其基本思想爲:先預先向系統申請一塊內存,此後須要申請內存時,直接到預先分配的內存中申請。ide

那麼這樣作的目的是什麼呢?工具

(1)避免了頻率地進行malloc/new和free/delete操做,同時對於內存管理變得簡單,對於內存的釋放工做交給Arena。源碼分析

(2)避免形成大量的內存碎片。(還需去了解一下)ui

      

下面看具體的源碼分析:spa

Arena定義:code

class Arena {
 public:
  Arena();
  ~Arena();

  // Return a pointer to a newly allocated memory block of "bytes" bytes.
  char* Allocate(size_t bytes);

  // Allocate memory with the normal alignment guarantees provided by malloc
  char* AllocateAligned(size_t bytes);

  // Returns an estimate of the total memory usage of data allocated
  // by the arena (including space allocated but not yet used for user
  // allocations).
  size_t MemoryUsage() const {
    return blocks_memory_ + blocks_.capacity() * sizeof(char*);
  }

 private:
  char* AllocateFallback(size_t bytes);
  char* AllocateNewBlock(size_t block_bytes);

  // Allocation state
  char* alloc_ptr_;
  size_t alloc_bytes_remaining_;

  // Array of new[] allocated memory blocks
  std::vector<char*> blocks_;

  // Bytes of memory in blocks allocated so far
  size_t blocks_memory_;

  // No copying allowed
  Arena(const Arena&);
  void operator=(const Arena&);
};

Arena提供兩種分配方式:所分配的內存嚴格對齊、不必定嚴格對齊的分配方式。每次預先分配的4K(爲何是4K?)保存到blocks_ vector中,最後統一釋放。這種內存管理方式是具備必定的適用範圍,如需不斷分配小內存,最終一併全釋放的場景。對於leveldb來講,memtable剛好就是這樣的,每次向memtable中insert一條k/v時,就申請一塊內存,當memtable被flush到磁盤且再也不使用時,將整個memtable釋放掉。orm

inline char* Arena::Allocate(size_t bytes) {
  // The semantics of what to return are a bit messy if we allow
  // 0-byte allocations, so we disallow them here (we don't need
  // them for our internal use).
  assert(bytes > 0);
  if (bytes <= alloc_bytes_remaining_) {
    char* result = alloc_ptr_;
    alloc_ptr_ += bytes;
    alloc_bytes_remaining_ -= bytes;
    return result;
  }
  return AllocateFallback(bytes);   //預先分配的不足
}

 

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;
}

 

下面來看下嚴格對齊的分配方式blog

char* Arena::AllocateAligned(size_t bytes) {
  const int align = sizeof(void*);    // We'll align to pointer size
  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);  ///計算出alloc_ptr_ % align 
size_t slop
= (current_mod == 0 ? 0 : align - current_mod); ///對齊還須要向前移動的大小
size_t needed
= bytes + slop;
char* result; if (needed <= alloc_bytes_remaining_) { result = alloc_ptr_ + slop; alloc_ptr_ += needed; alloc_bytes_remaining_ -= needed; } else { // AllocateFallback always returned aligned memory result = AllocateFallback(bytes); } assert((reinterpret_cast<uintptr_t>(result) & (align-1)) == 0); return result; } char* Arena::AllocateNewBlock(size_t block_bytes) { char* result = new char[block_bytes]; blocks_memory_ += block_bytes; blocks_.push_back(result); return result; }
相關文章
相關標籤/搜索