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