一. 內(nèi)存池的優(yōu)勢
1. 直接使用系統(tǒng)調(diào)用的弊端
- 調(diào)用malloc/new,系統(tǒng)需要根據(jù)“最先匹配”零抬、“最優(yōu)匹配”或其他算法在內(nèi)存空閑塊表中查找一塊空閑內(nèi)存立美,調(diào)用free/delete,系統(tǒng)可能需要合并空閑內(nèi)存塊越锈,這些會產(chǎn)生額外開銷
- 頻繁使用時會產(chǎn)生大量內(nèi)存碎片仅淑,從而降低程序運行效率(對于內(nèi)存碎片問題上這篇博文有著不一樣的看法晕拆,有一定道理萝风,可以參考一下_)
- 容易造成內(nèi)存泄漏
2. 內(nèi)存池的優(yōu)點
- 比malloc/free進行內(nèi)存申請/釋放的方式快(向內(nèi)存池請求嘀掸,而不是直接向操作系統(tǒng)請求)
- 不會產(chǎn)生或很少產(chǎn)生堆碎片
- 可避免內(nèi)存泄漏
二. Arena內(nèi)存池的實現(xiàn)
1. Arena類的成員變量
// 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_;
Arena類的成員變量還是相對比較簡單的,alloc_ptr_
表示當(dāng)前內(nèi)存塊(block)偏移量指針规惰,也就是未使用內(nèi)存的首地址睬塌。 alloc_bytes_remaining_
表示當(dāng)前塊所未使用的空間大小。blocks_
是一個vector
用來存儲每一次向系統(tǒng)請求的分配的內(nèi)存指針歇万。 memory_usage_
則是用來記錄Arena
類內(nèi)存使用情況的揩晴。
2. Arena類的成員函數(shù)
a. 構(gòu)造函數(shù)
Arena::Arena() : memory_usage_(0) {
alloc_ptr_ = NULL; // First allocation will allocate a block
alloc_bytes_remaining_ = 0;
}
非常簡單的一個構(gòu)造函數(shù),負責(zé)初始化一些變量贪磺,注意剛開始是沒有立刻分配內(nèi)存的
b. 析構(gòu)函數(shù)
Arena::~Arena() {
for (size_t i = 0; i < blocks_.size(); i++) {
delete[] blocks_[i];
}
}
析構(gòu)函數(shù)負責(zé)將從系統(tǒng)中申請的內(nèi)存返還給系統(tǒng)硫兰。
c. Allocate() && AllocateFallback() && AllocateNewBlock()
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;
}
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;
}
Allocate
是Arena向外界提供的接口,該函數(shù)會調(diào)用AllocateFallback() && AllocateNewBlock() 這兩個私有函數(shù)寒锚。
- 如果需求的內(nèi)存小于剩余的內(nèi)存瞄崇,那么直接從內(nèi)存池中獲取
- 如果需求的內(nèi)存大于剩余的內(nèi)存,而且大于1K壕曼,則給這內(nèi)存單獨分配一塊bytes(函數(shù)參數(shù))大小的內(nèi)存
- 如果需求的內(nèi)存大于剩余的內(nèi)存苏研,而且小于4096/4,則重新分配一個內(nèi)存塊腮郊,默認大小4096摹蘑,用于存儲數(shù)據(jù)。原有的剩余空間浪費掉轧飞。
d. AllocateAligned()
char* Arena::AllocateAligned(size_t bytes) {
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;
}
Arena
還提供了字節(jié)對齊內(nèi)存分配衅鹿,一般情況是8個字節(jié)對齊分配撒踪。對齊內(nèi)存的好處簡單的說就是加速內(nèi)存訪問。具體實現(xiàn)源碼已經(jīng)寫的非常詳細了大渤,稍微有點疑惑的地方也都有了注釋制妄。
e. MemoryUsage()
size_t MemoryUsage() const {
return reinterpret_cast<uintptr_t>(memory_usage_.NoBarrier_Load());
}
Arena
最后一個對外接口是返回這個內(nèi)存池分配總的內(nèi)存大小。
icecity96 liuhiter@gmail.com