2013-04-06 12 views
5

背景boost.pool如何實現分配內存的重用?

previous questionboost.pool導致我詳細調查boost.pool,現在我有一個補充問題,最後確定我的理解。

前奏

This reference狀態下面對對象池模式:

對象池模式是一種軟件造物設計模式 使用保持一組初始化的對象就可以使用,而不是按照需求分配和銷燬它們。

從我可以告訴,boost.pool(簡體)實現了由主要基於一個element_type的大小內存分配和管理手段的對象池模式,並返回一個簡單的指針已分配對象:

element_type * malloc(); 
void free(element_type * p); 

一個簡單的升壓實施例還表明,這是沒有必要明確free獲取的元素:

X * const t = p.malloc(); 
... // Do something with t; don't take the time to free() it. 

問題

據我所知,所分配的內存將被安全釋放的池對象的破壞,但如何池知道當客戶端獲取的內存塊已經被釋放回池,是可重複使用的,如果它的接口返回一個直接指向element_type的指針,但仍然不需要調用free()?即如果升壓池無法確定內存是否仍在使用中,如何重新使用該內存?如果它不重用這個內存,這甚至被認爲是與維基引用所解釋的模式相同的模式嗎?

回答

7

如何助推池如果不能肯定, 內存是不是仍然在使用重新使用該內存?

它不能。實際上它不會重用那個內存。它只能保證你在池被銷燬時不會泄漏

如果它不重複使用這個內存,這甚至被認爲是與維基引用解釋相同的模式?

article你聯繫說:對象池可以提供的情況下,一個顯著的性能提升,其中初始化類實例的成本高

雖然從Boost pool介紹:池一般都使用時,有是很多小物件的分配和釋放。

所以不,他們是不一樣的模式。其中一個旨在重新使用昂貴的來構建(線程,opengl資源等)。另一個是爲了管理很多小對象,給予你比標準分配器更多的控制權。

正如你指出的,還有用池的方法有兩種:

  1. 作爲一個allocator,調用malloc()/ free()的適當時候。這是基本的池分配器使用,它有助於減少內存碎片
  2. 構建大量的臨時對象,不用費心去刪除它們。

第二種情況的示例:假設您有一個圖類,其中每個節點使用指針存儲其鄰居。現在,您必須對圖形進行深層複製。您將分配一堆新的節點,從舊到新的複製數據,但現在你必須初始化鄰居指針,所以你需要從舊指向新指針的地圖:

std::map<node*,node*> old_ptr_to_new_ptr; 

這是池分配器很有用的一個很好的例子(我不會詳細討論如何在std容器中使用池分配器):很多小的對象(地圖節點)將被一起刪除。

+0

感謝兩種模式之間的比較,這完全回答了我的問題。 – jwalk 2013-04-06 15:38:24

6

升壓池庫提供STL allocators分配相同的類型的對象時(相對於std::allocator簡單的使用newdelete),更爲高效。這就是Stroustrup或Alexandrescu所稱的small-object allocator

作爲任何自定義分配器類,它基本上與四個獨立的函數一起工作:分配,釋放,構造和銷燬。我認爲他們的名字是不言自明的(除非你對分配和建設感到困惑)。要從池中獲取新對象,請先撥打allocate(1)以獲取指針,然後在該指針ptr上撥打construct(ptr, value),將其構建爲value(或移動)的副本。當你想刪除那個對象時,你會做相反的事情。這些是所有STL容器使用底層來分配 - 構造 - 銷燬 - 釋放其對象的機制。

你不應該相信你提到的維基百科文章(而且通常也不是這樣),它措辭很差,使用非常模糊和不準確的語言,並且在對象池模式上的視圖有些狹窄。順便說一下,引用維基百科是毫無價值的,你沒有寫它,沒有理由相信它,總是去源頭。

wiki中描述的模式(特別是在源文章中)與boost池分配器嘗試完成的目標有着非常不同的目標。正如維基中所描述的那樣,重點在於重用對象而不會真正銷燬對象(例如,thread-pool就是一個很好的例子,因爲經常創建和銷燬線程會很昂貴,並且源文章有興趣將數據庫服務提供者類似的原因)。在boost池分配器中,重點在於避免調用堆(freestore)來分配許多小對象,這是堆不能非常有效地執行的任務,並且會導致它變得碎片化。它可能應該被稱爲「小對象分配器」,以避免混淆。

如何池知道當客戶端獲取的內存塊已經被釋放回池,是可重複使用的,如果它的界面手中奪回直接指向ELEMENT_TYPE,但一個調用free()仍然是不需要?

我不認爲它可以。我相信這個故事是這樣的。你可以選擇只從池中分配一堆對象,而不用釋放它們,並且這仍然是安全的,因爲當你銷燬池分配器時,它的所有內存都會被刷新,包括你所有的對象留在池中徘徊。這就是「不需要釋放對象」的意思,只是如果忘記釋放池中的所有對象,應用程序不會泄漏超出池分配器對象的生命週期的內存。但是,如果你不告訴池分配器釋放你不再需要的對象(因此可以被重用),它將不能重用這些內存槽,這是不可能的(因爲這是不可能的分配器不會提供任何能夠跟蹤分配對象的特殊智能指針)。

如果boost池無法確定內存是否仍在使用中,如何重新使用此內存?

如果不能確定內存是否仍在使用中,則無法重新使用內存。任何會使「如果不確定就不再需要對象」這樣魯莽的事情的代碼段將是一段毫無價值的代碼,因爲它顯然會有未定義的行爲,並且程序員也不可能使用它。

如果它不重複使用這個內存,這甚至被認爲是與維基引用解釋相同的模式?

不,它沒有實現在wiki中解釋的內容。你必須習慣術語有時以不幸的方式衝突的事實。更常見的是將什麼boost池實現爲「內存池」或「小對象分配器」。這是一個針對構建和複製相當便宜的小對象而優化的結構。因爲堆(freestore)是爲更大塊的內存量身定做的,而且往往會試圖找到一個小物體的地方,所以用它來達到這個目的不是一個好主意,並且會導致堆碎片化。因此,池分配器基本上用分配相同類型的許多小對象時更有效的方式替換堆。它不會重用對象,但它可以重用已釋放的內存,就像堆一樣。它通常從堆中分配它的內存(它分配的)作爲一個大的連續塊(​​例如,與std::vector)。在適當的時候使用小對象分配器還有許多其他性能優勢。但是,激勵池實現與維基中描述的實際上非常不同。這裏是池分配器的實現者的描述是好的:

使用一個帶游泳池的好地方,是在許多(非連續)的小物件可以在堆中分配的情況下,或者如果分配和釋放相同大小的對象重複發生。

+0

我同意這兩種模式之間的術語似乎有點名衝突。但是你是對的,對模式的理解應該以其目的爲基礎,而不是基於其目的。 – jwalk 2013-04-06 15:40:41

相關問題