Loki是由 Andrei 編寫的一個與《Modern C++ Design》(C++設計新思惟)一書配套發行的C++代碼庫。其中有兩個文件 SmallObj.h
、SmallObj.cpp
進行內存管理,能夠單獨進行使用算法
SmallObj 文件中有三個類:chunk
, FixedAllocator
和 SmallObjAllocator
。其中SmallObjAllocator
位於最頂層供應用程序調用 cookie
Chunk 是類層次結構中最底層管理內存塊的類,它負責向操做系統進行內存申請oop
1. Init(), 使用 operator new 申請一段內存 chunk, 並使用 pData_ 指向 chunk 2. Reset(), 對 pData_ 指向的內存進行分割。[數組代替鏈表,索引代替指針] [與嵌入式指針相似]每一塊 block 的第一個字節存放的是下一個可用的 block 距離起始位置 pData_ 的偏移量(以 block 大小爲單位) 3. Relese(), 向操做系統歸還內存 -- 1. blockSize、blocksblock, block 大小及數量 2. firstAvailableBlock_,當前可用內存塊的偏移量 3. blocksAvailable,當前 chunk 中剩餘的 block 數量
unsigned char i = 0; unsigned char *p = pData; for(;i!=blocks; p+=blockSize) // 以 blockSize 爲間隔切分 chunk 爲 block *p = ++i; // 以 block 的第一個字節存儲下一個可用 block 索引
參數初始化後的 chunk
用索引對區塊進行管理[第一字節流水號]
FixedAllocate 負責管理一個具備相同大小 block 的 chunk 集合。它負責根據應用程序需求,建立特定大小的 chunk, 並放置在 vcector 中進行管理spa
void *FixedAllocator::allocate() { if (allocChunk_ == 0 || allocChunk_->blocksAvailable == 0) { // 目前沒有標定 chunk 或 該 chunk 已無可用區塊 Chunks::iterator i = chunks_.begin(); // 打算從頭找起 for (;; ++i) // 找遍每一個 chunk 直至找到擁有可用區塊者 { if (i == chunks_.end()) // 到達尾端,都沒找到 { // Initialize chunks_.push_back(Chunk()); // 產生 a new chunk 掛於末端; Chunk(),建立臨時對象拷貝至容器而後結束生命 Chunk& newChunk = chunks_.back(); // 指向末端 newChunk.Init(blockSize_, numBlocks_); // 設置好索引 allocChunk_ = &newChunk; // 標定,稍後將對此 chunk 取區塊 deallocChunk_ = &chunks_.front(); // 另外一標定 break; } if (i->blocksAvailable_ > 0) { // current chunk 有可用區塊 allocChunk_ = &*i; // 取地址 break; } } } // allocChunk_, 在此 chunk 找到可用區塊,下次就優先今後找起 return allocChunk_->Allocate(blockSize_); // 向這個 chunk 取區塊 }
allocChunk_
標記最近一次知足分配動做的 chunk, 當下次再有分配需求時,優先檢查此 chunk
deallochunk_
依靠數據的內聚性和區域性原則 當某一 chunk 發生內存回收時,下次回收也可能發生在此 chunk 上。 以此儘可能避免 `void Deallocate(void *p)`中 p 落在哪個 chunks 的遍歷查找動做(類比於上述代碼 for )
deallocChunk_ = &chunks_.front()
vector 在進行 insert 時,可能會致使內存增加,內存增加時元素將從舊空間拷貝到新的空間,此時過去 deallocChunk_ 指向的地址將失效,所以須要對 deallocChunk_ 從新標定
咱們須要根據歸還內存的地址,把這塊內存回收到對應的 chunk 中操作系統
void FixedAllocator::Deallocate(void *p) { deallocChunk = VicinityFind(p); DoDeallocate(); }
根據內存使用的區域性,採用臨近查找法肯定 p 所對應的 chunk .net
1. 已知每一塊 chunk 指向內存空間的地址 p_Data_ 2. 已知每一塊內存空間的大小 numblocks_ * blocksize 3. 由此可計算出每一塊 chunk 指向內存的地址範圍 [p_Data_, p_Data_ + numblocks_ * blocksize] 4. 由此可計算出歸還的內存 p 屬於哪個 chunk --- 查找思想:VicinityFind 採用臨近分頭查找的算法,從上一次 dealloChunk_ 的位置出發進行上下兩頭查找 (內存分配一般是個容器服務的,而容器元素連續建立時,一般就從同一個 chunk 得到連續的地址空間,歸還的時候固然也是歸還到同一塊 chunk。經過對上一次歸還 chunk 的記錄,儘可能避免遍歷搜索,提升了查找定位速度) 在上述實現中,若是 p 並不是當初由此係統得到,確定找不到對應的 chunk,因而陷入死循環。在新版本中已修復此問題
完成實際的內存回收設計
1. deallocChunk->Deallocate(p, blockSize_); 由 FixedAllocator::chunk::Deallocate(void *p, std::size_t blockSize) 完成底層的內存回收 2. 當 deallockChunk_->blocksAvailable_ = numBlocks_ 時表示當前內存能夠歸還給操做系統 3. 延遲歸還機制,把空的 chunk 交換到 vector 尾部,只有出現兩個空的 chunk 時,纔會發生真正的內存歸還動做(表中標註①②③)
SmallObjAllocator 負責管理具備不一樣 block size 的 FixedAllocate 的vector 集合指針
void* SmallObjAllocator::Allocate(std::size_t numBytes) { if (numBytes > maxObjectSize_) return operator new(numBytes); if (pLastAlloc_ && pLastAlloc_->BlockSize() == numBytes) { return pLastAlloc_->Allocate(); } //找到第一個 >= numBytes 的位置 Pool::iterator i = std::lower_bound(pool_.begin(), pool_.end(), numBytes); //沒找到相同的,就從新建立一個 FixedAllocator if (i == pool_.end() || i->BlockSize() != numBytes) { i = pool_.insert(i, FixedAllocator(numBytes)); pLastDealloc_ = &*pool_.begin(); } pLastAlloc_ = &*i; return pLastAlloc_->Allocate(); }
1. 當應用程序請求的 numBytes 大於 maxObjectSize_ 時交由 operator new 處理 2. pLastAlloc_ 記錄上次分配 block 的 FixedAllocator object。若是本次申請的 block size 等於上次分配的 block size,就直接使用同一個 FixedAllocator object,以此盡力避免查找動做(最佳客戶是容器,容器的元素大小是相同的) 3. 若是本次需求的 block size 不等於上次分配的 block size,就遍歷查找大小相等的 FixedAllocator object。若是沒有找到,就插入新的 FixedAllocator object。同時爲了不 vector 擴容引發的內存從新分配,對 pLastDealloc_ 重定位
void SmallObjAllocator::Deallocate(void* p, std::size_t numBytes) { if (numBytes > maxObjectSize_) return operator delete(p); if (pLastDealloc_ && pLastDealloc_->BlockSize() == numBytes) { pLastDealloc_->Deallocate(p); return; } Pool::iterator i = std::lower_bound(pool_.begin(), pool_.end(), numBytes); assert(i != pool_.end()); assert(i->BlockSize() == numBytes); pLastDealloc_ = &*i; pLastDealloc_->Deallocate(p); }
std::allocator | loki::allocator |
---|---|
不會向操做系統歸還內存 | 延遲機制內存歸還 |
服務於 8-128(每次增長 8byte) 內存塊,申請不知足時RoundUp調整 | 爲不大於最大 block size 的全部 block size 服務 |