C++記憶體管理變革(4):boost::object_pool與gc allocator
C++記憶體管理變革(4): boost::object_pool
許式偉 ()
2007-4-21
這篇文章拖的有點久了。NeutralEvil 在3個月之前就在催促我繼續寫了。只是出於WinxGui完整性的考慮,我一直在刻意優先去補充其它方面的文章,而不是讓人去誤會WinxGui是一個記憶體管理庫了。:)
言歸正傳。我們在記憶體池(MemPool)技術詳解已經介紹了boost::pool元件。從記憶體管理觀念的變革來看,這是是一個傳統的MemPool元件,儘管也有一定的改進(但只是效能上的改進)。但boost::object_pool不同,它與我在C++記憶體管理變革強調的觀念非常吻合。可以認為,boost::object_pool是一種不通用的gc allocator元件。
我已經多次提出的概念。這裡仍然需要強調一下,所謂gc allocator,是指具垃圾回收能力的allocator。C++記憶體管理變革(1) 中我們引入了這個概念,但是沒有明確gc allocator一詞。
boost::object_pool記憶體管理觀念
boost::object_pool的了不起之處在於,這是C++從庫的層次上頭一次承認,程式設計師在記憶體管理上是會犯錯誤的,由程式設計師來確保記憶體不洩漏是困難的。boost::object_pool允許你忘記釋放記憶體。我們來看一個例子:
class X { … };void func()
{
boost::object_pool
X* obj1= alloc.construct();
X* obj2= alloc.construct();
alloc.destroy(obj2);
}
如果boost::object_pool只是一個普通的allocator,那麼這段程式碼顯然存在問題,因為obj1的解構函式沒有執行,申請的記憶體也沒有釋放。
但是這段程式碼是完全正常的。是的,obj1的析構確實執行了,所申請記憶體也被釋放了。這就是說,boost::object_pool既支援你手工釋放記憶體(通過主動呼叫object_pool::destroy),也支援記憶體的自動回收(通過object_pool::~object_pool析構的執行)。這正符合gc allocator的規格。
注:記憶體管理更好的說法是物件管理。記憶體的申請和釋放更確切的說是物件的建立和銷燬。但是這裡我們不刻意區分這兩者的差異。
boost::object_pool與AutoFreeAlloc
我們知道,AutoFreeAlloc不支援手工釋放,而只能等到AutoFreeAlloc物件析構的時候一次性全部釋放記憶體。那麼,是否可以認為boost::object_pool是否比AutoFreeAlloc更加完備呢?
其實不然。boost::object_pool與AutoFreeAlloc都不是完整意義上的gc allocator。AutoFreeAlloc因為它只能一次性釋放,故此僅僅適用特定的用況。然而儘管AutoFreeAlloc不是普適的,但它是通用型的gc allocator。而boost::object_pool只能管理一種物件,並不是通用型的allocator,侷限性其實更強。
boost::object_pool的實現細節
大家對boost::object_pool應該已經有了一個總體的把握。現在,讓我們深入到object_pool的實現細節中去。
在記憶體池(MemPool)技術詳解中,我們介紹boost::pool元件時,特意提醒大家留意pool::ordered_malloc/ordered_free函式。事實上,boost::object_pool的malloc/construct, free/destroy函式呼叫了pool::ordered_malloc, ordered_free函式,而不是pool::malloc, free函式。
讓我們解釋下為什麼。
其實這其中的關鍵,在於object_pool要支援手工釋放記憶體和自動回收記憶體(並自動執行解構函式)兩種模式。如果沒有自動析構,那麼普通的MemPool就足夠了,也就不需要ordered_free。既然有自動回收,同時又存在手工釋放,那麼就需要區分記憶體塊(MemBlock)中哪些結點(Node)是自由記憶體結點(FreeNode),哪些結點是已經使用的。對於哪些已經是自由記憶體的結點,顯然不能再呼叫物件的解構函式。
我們來看看object_pool::~object_pool函式的實現:
template <typename T, typename UserAllocator>
object_pool<T, UserAllocator>::~object_pool()
{
// handle trivial caseif (!this->list.valid())
return;
details::PODptr<size_type> iter =this->list;
details::PODptr<size_type> next = iter;
// Start ’freed_iter’ at beginning of free listvoid* freed_iter =this->first;
const size_type partition_size =this->alloc_size();
do
{
// increment next next = next.next();
// delete all contained objects that aren’t freed
// Iterate ’i' through all chunks in the memory blockfor (char* i = iter.begin(); i != iter.end(); i += partition_size)
{
// If this chunk is freeif (i == freed_iter)
{
// Increment freed_iter to point to next in free list freed_iter = nextof(freed_iter);
// Continue searching chunks in the memory blockcontinue;
}
// This chunk is not free (allocated), so call its destructor static_cast<T *>(static_cast<void*>(i))->~T();
// and continue searching chunks in the memory block }
// free storage UserAllocator::free(iter.begin());
// increment iter iter = next;
} while (iter.valid());
// Make the block list empty so that the inherited destructor doesn’t try to
// free it again.this->list.invalidate();
}
這段程式碼不難理解,object_pool遍歷所有申請的記憶體塊(MemBlock),並遍歷其中所有結點(Node),如果該結點不出現在自由記憶體結點(FreeNode)的列表(FreeNodeList)中,那麼,它就是使用者未主動釋放的結點,需要進行相應的析構操作。
現在你明白了,ordered_malloc是為了讓MemBlockList中的MemBlock有序,ordered_free是為了讓FreeNodeList中的所有FreeNode有序。而MemBlockList, FreeNodeList有序,是為了更快地檢測Node是自由的還是被使用的(這實際上是一個集合求交的流程,建議你看看std::set_intersection,它定義在STL的<algorithm>中)。