定長內存池之BOOST::pool
內存池可有效降低動態申請內存的次數,減少與內核態的交互,提升系統性能,減少內存碎片,增加內存空間使用率,避免內存泄漏的可能性,這么多的優點,沒有理由不在系統中使用該技術。
內存池分類:
1、 不定長內存池。典型的實現有apr_pool、obstack。優點是不需要為不同的數據類型創建不同的內存池,缺點是造成分配出的內存不能回收到池中。這是由于這種方案以session為粒度,以業務處理的層次性為設計基礎。
2、 定長內存池。典型的實現有LOKI、BOOST。特點是為不同類型的數據結構分別創建內存池,需要內存的時候從相應的內存池中申請內存,優點是可以在使用完畢立即把內存歸還池中,可以更為細粒度的控制內存塊。
與變長的相比,這種類型的內存池更加通用,另一方面對于大量不同的數據類型環境中,會浪費不少內存。但一般系統主要的數據結構都不會很多,并且都是重復申請釋放使用,這種情況下,定長內存池的這點小缺點可以忽略了。
Boost庫的pool提供了一個內存池分配器,用于管理在一個獨立的、大的分配空間里的動態內存分配。Boost庫的pool主要適用于快速分配同樣大小的內存塊,尤其是反復分配和釋放同樣大小的內存塊的情況。使用pool內存池主要有以下兩個優點:
1. 能夠有效地管理許多小型對象的分配和釋放工作,避免了自己去管理內存而產生的內存碎片和效率低下問題。
2. 告別程序內存泄漏的煩惱,pool庫會在內部對內存自動進行管理,避免了程序員一不小心而造成的內存泄漏問題。
pool庫主要提供了四種內存池接口,分別是pool、object_pool、singleton_pool和pool_allocator/fast_pool_allocator。
1)pool
基本的定長內存池
#include <boost/pool/pool.hpp>
typedef struct student_st
{
char name[10];
int age;
}CStudent;
int main()
{
boost::pool<> student_pool(sizeof(CStudent));
CStudent * const obj=(CStudent *)student_pool.malloc();
student_pool.free(obj);
return 0;
}
pool的模版參數只有一個分配子類型,boost提供了兩種default_user_allocator_new_delete/default_user_allocator_malloc_free,指明申請釋放內存的時候使用new/delete,還是malloc/free,默認是default_user_allocator_new_delete。構造函數有2個參數:nrequested_size,nnext_size。nrequested_size是block的大小(因為void*保存序號,因此boost內置了block的最小值,nrequested_size過小則取內置值),nnext_size是simple_segregated_storage中內存不足的時候,申請的block數量,默認是32。最全面的實例化pool類似這樣:boost::pool<boost::default_user_allocator_malloc_free> student_pool(sizeof(CStudent),255);
pool提供的函數主要有:
malloc/free 基于add_block/malloc/free實現,高效
ordered_malloc/ordered_free 基于add_ordered_block/malloc/ordered_free實現,在pool中無任何意義,切勿使用。
release_memory/purge_memory 前者釋放池中未使用內存,后者釋放池中所有內存。另池析構也會釋放內存
2)object_pool
對象內存池,這是最失敗的一個內存池設計。
#include <boost/pool/object_pool.hpp>
class A{
public:
A():data_(0){}
private:
int data_;
};
int main()
{
boost::object_pool<A> obj_pool;
A *const pA=obj_pool.construct();
obj_pool.destroy(pA);
return 0;
}
object_pool繼承至pool,有兩個模版參數,第一個就是對象類型,第二個是分配子類型,默認同pool是default_user_allocator_new_delete。構造函數參數只有nnext_size,意義以及默認值同pool。最全面的實例化object_pool類似這樣:boost::pool<A,boost::default_user_allocator_malloc_free> obj_pool(255);
object_pool提供的函數主要有(繼承至父類的略): malloc/free 復寫pool的malloc/free,add_ordered_block/malloc/ordered_free實現
construct/destroy 基于本類的malloc/free實現,額外調用默認構造函數和默認析構函數。
~object_pool 單獨拿出這個說下,若析構的時候有對象未被destroy,可以檢測到,釋放內存前對其執行destroy
為什么boost::object_pool要設計成這樣?能調用構造函數和析構函數顯然不是boost::object_pool類設計的出發點,因為構造函數只能執行默認構造函數(首次發表錯誤:可以調用任意的構造函數,參見代碼文件:boost/pool/detail/pool_construct.inc和boost/pool/detail/pool_construct_simple.inc,感謝eXile指正),近似于無,它的重點是內存釋放時候的清理工作,這個工作默認的析構函數就足夠了。apr_pool內存池中就可以注冊內存清理函數,在釋放內存的時刻執行關閉文件描述符、關閉socket等操作。boost::object_pool也想實現同樣的功能,因此設計了destroy這個函數,而同時為了防止用戶遺漏掉這個調用,而又在內存池析構的時候進行了檢測回收。為了這個目的而又不至于析構object_pool的時間復雜度是O(n平方),boost::object_pool付出了沉重的代價,在每次的destoy都執行排序功能,時間復雜度O(n),最后析構的時間復雜度是O(n),同樣為了這個目的,從simple_segregated_storage增加了add_ordered_block/ordered_free,pool增加了ordered_malloc/ordered_free等累贅多余的功能。
基于上面討論的原因,boost::object_pool被設計成了現在的樣子,成了一個雞肋類。類的設計者似乎忘記了內存池使用的初衷,忘記了內存池中內存申請釋放的頻率很高,遠遠大于內存池對象的析構。如果你依然想使用類似于此的內存清理功能,可以在boost::object_pool上修改,不復寫malloc/free即可,重寫object_pool的析構,簡單釋放內存就好,因此析構object_pool前不要忘記調用destroy,這也是使用placement new默認遵守的規則,或者保持以前的析構函數,犧牲析構時的性能。placement new的作用是為已經申請好的內存調用構造函數,使用流程為(1)申請內存buf(2)調用placement new:new(buf)construtor()(3)調用析構destructor()(4)釋放內存buf。#include<new>可以使用placement new。
3)singleton_pool
pool的加鎖版本。
#include <boost/pool/singleton_pool.hpp>
typedef struct student_st
{
char name[10];
int age;
}CStudent;
typedef struct singleton_pool_tag{}singleton_pool_tag;
int main()
{
typedef boost::singleton_pool<singleton_pool_tag,sizeof(CStudent)> global;
CStudent * const df=(CStudent *)global::malloc();
global::free(df);
return 0;
}
singleton_pool為單例類,是對pool的加鎖封裝,適用于多線程環境,其中所有函數都是靜態類型。它的模版參數有5個,tag:標記而已,無意義;RequestedSize:block的長度;UserAllocator:分配子,默認還是default_user_allocator_new_delete;Mutex:鎖機制,默認值最終依賴于系統環境,linux下是pthread_mutex,它是對pthread_mutex_t的封裝;NextSize:內存不足的時候,申請的block數量,默認是32。最全面的使用singleton_pool類似這樣:typedef boost::singleton_pool<singleton_pool_tag,sizeof(CStudent),default_user_allocator_new_delete,details::pool::default_mutex,200> global;
它暴露的函數和pool相同。
4)pool_allocator/fast_pool_allocator
stl::allocator的替換方案。兩者都是基于singleton_pool實現,實現了stl::allocator要求的接口規范。兩者的使用相同,區別在于pool_allocator的內部實現調用了ordered_malloc和ordered_free,可以滿足對大量的連續內存塊的分配請求。fast_pool_allocator 的內部實現調用了malloc和free,比較適合于一次請求單個大內存塊的情況,但也適用于通用分配,不過具有一些性能上的缺點。因此推薦使用后者。
#include <boost/pool/pool_alloc.hpp>
#include <vector>
typedef struct student_st
{
char name[10];
int age;
}CStudent;
int main()
{
std::vector<CStudent *,boost::fast_pool_allocator<CStudent *> > v(8);
CStudent *pObj=new CStudent();
v[1]=pObj;
boost::singleton_pool<boost::fast_pool_allocator_tag,sizeof(CStudent *)>::purge_memory();
return 0;
}
fast_pool_allocator的模版參數有四個:類型,分配子,鎖類型,內存不足時的申請的block數量,后三者都有默認值,不再說了。它使用的singleton_pool的tag是boost::fast_pool_allocator_tag。
總結:boost::pool小巧高效,多多使用,多線程環境下使用boost::singleton_pool,不要使用兩者的ordered_malloc/ordered_free函數。boost::object_pool不建議使用,可以改造后使用。pool_allocator/fast_pool_allocator推薦使用后者。
參考資料:
boost官方網站: http://www.boost.org/
RFID管理系統集成商 RFID中間件 條碼系統中間層 物聯網軟件集成