1、預備知識——內存池
ide
內存池是爲了使內存分配的效率獲得提高而採用的一種方法,而且不多產生堆碎片,能夠避免內存泄漏。
函數
簡單來講,就是每次申請的內存都放入一個容器當中,每次須要申請的內存先看是否能夠從內存池中直接分配,若是不夠,那麼先申請一塊新的內存放入內存池中,而後再進行分配。最後釋放的時候,只須要釋放這個容器管理的內存便可。
源碼分析
2、源碼分析
性能
1. arena.h優化
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. size_t MemoryUsage() const { return reinterpret_cast<uintptr_t>(memory_usage_.NoBarrier_Load()); } 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_; //整個內存池 // Total memory usage of the arena. port::AtomicPointer memory_usage_; //內存總的使用狀況,即申請了多大的內存 // No copying allowed Arena(const Arena&); void operator=(const Arena&); };
整個Arena類中,只有三個接口函數能夠調用,他們分別是Allocate,申請內存;AllocateAligned 申請字節對齊的內存;MemoryUsage, 內存使用狀況,三個函數。下面分別對三個函數進行說明ui
1.1 Allocate函數
spa
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); }
若是申請的內存大小不比當前內存池中可用內存的大時,那麼就將直接從當前內存池中得到內存,不然,就須要從新申請內存。
3d
過程以下:指針
上圖是初始內存池
orm
上圖是bytes<alloc_bytes_remaining_時,申請內存的狀況。
當bytes>alloc_bytes_remaining_時,這時就調用了AllocateFallback函數。
1.2 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; }
從代碼能夠看出,當bytes 比 BlockSize/4, 即大於1024時,那麼就直接從新申請須要大小的內存。可是,若是申請的內存小於1024時,這時候,就是直接申請一塊4096的新內存,而後將可用的內存大小設置爲4096,而後再進行指針的偏移和可用內存的減少。
這裏有兩個特別須要注意的地方:
1) 大於1024時申請的內存是在以前申請的內存後面,若是下一次申請的內存小於alloc_bytes_remaining_,那麼仍是用當前內存池當中的可用內存。
2) 當申請的內存處於大於alloc_bytes_remaining_,而且小於1024時,那麼這個時候就會從新申請一塊4096大小的內存,而且將alloc_bytes_emaining_的大小更新爲4096。這裏意味着浪費了一塊小於1024大小的內存,在查詢資料之後發現,做者浪費就浪費了,只要性能Ok就好了。所以,若是是本身使用的時候,這裏屬於能夠優化的部分。
1.3 AllocateNewBlock
char* Arena::AllocateNewBlock(size_t block_bytes) { char* result = new char[block_bytes]; blocks_.push_back(result); memory_usage_.NoBarrier_Store( reinterpret_cast<void*>(MemoryUsage() + block_bytes + sizeof(char*))); return result; }
申請一塊新的內存,並將這塊內存放入內存池容器中,而後更新內存使用狀況。
1.4 AllocateAligned函數
char* Arena::AllocateAligned(size_t bytes) { //判斷一個數是否是2的指數次冪的奇淫巧技 const int align = (sizeof(void*) > 8) ? sizeof(void*) : 8; 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; 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; }
這個函數是用來申請內存對齊的函數。
交流QQ羣:199546072