目錄html
QQ:386859647
微信:herelsp
轉自:http://tech.it168.com/a2011/0726/1223/000001223399_all.shtmlios
- Boost庫是一個可移植的開源C++函數庫,鑑於STL(標準模板庫)已經成爲C++語言的一個組成部分,能夠絕不誇張的說,Boost是目前影響最大的通用C++庫。Boost庫由C++標準委員會庫工做組成員發起,其中有些內容有望成爲下一代C++標準庫內容,是一個「準」標準庫。
- Boost內存池,即boost.pool庫,是由Boost提供的一個用於內存池管理的開源C++庫。做爲Boost中影響較大的一個庫,Pool已經被普遍使用。
池 是在計算機技術中常用的一種設計模式,其內涵在於:將程序中須要常用的核心資源先申請出來,放到一個池內,由程序本身管理,這樣能夠提升資源的使用效率,也能夠保證本程序佔有的資源數量。
常用的池技術包括內存池、線程池和鏈接池等,其中尤之內存池和線程池使用最多。
內存池(Memory Pool) 是一種動態內存分配與管理技術。
一般狀況下,程序員習慣直接使用 new、delete、malloc、free 等API申請分配和釋放內存,這樣致使的後果是:當程序長時間運行時,因爲所申請內存塊的大小不定,頻繁使用時會形成大量的內存碎片從而下降程序和操做系統的性能。
內存池則是在真正使用內存以前,先申請分配一大塊內存(內存池)留做備用,當程序員申請內存時,從池中取出一塊動態分配,當程序員釋放內存時,將釋放的內存再放入池內,並儘可能與周邊的空閒內存塊合併。若內存池不夠時,則自動擴大內存池,從操做系統中申請更大的內存池。程序員
早期的內存池技術是爲了專門解決那種頻繁申請和釋放相同大小內存塊的程序,所以早期的一些內存池都是用相同大小的內存塊鏈表組織起來的。
Boost的內存池則對內存塊的大小是否相同沒有限制,所以只要是頻繁動態申請釋放內存的長時間運行程序,都適用Boost內存池。這樣能夠有效減小內存碎片並提升程序運行效率。算法
Boost的pool庫是以C++頭文件的形式提供的,不須要安裝,也沒有lib或者dll文件,僅僅須要將頭文件包含到你的C++工程中就能夠了。Boost的最新版本能夠到 http://www.boost.org/ 下載。設計模式
正確的使用內存池的申請和釋放函數不會形成內存泄露,更重要的是,即便不正確的使用了申請和釋放函數,內存池中的內存也會在進程結束時被所有自動釋放,不會形成系統的內存泄露。數組
例如一個元素的內存大小爲A,那麼元素數組若包含n個元素,則該數組的內存大小必然是A*n,不會有多餘的內存來填充該數組。儘管每一個元素也許包含一些填充的東西。安全
這代表你仍可使用那些經過數組指針計算內存塊位置的算法。微信
這個快是機率意義上的,不是每一個時刻,每種內存池都比直接使用new或者malloc快。例如,當程序使用內存池時內存池剛好處於已經滿了的狀態,那麼此次內存申請會致使內存池自我擴充,確定比直接new一塊內存要慢。但在大部分時候,內存池要比new或者malloc快不少。多線程
分別用內存池和new連續申請和連續釋放大量的內存塊,比較其運行速度,代碼以下:函數
#include "stdafx.h" #include <iostream> #include <ctime> #include <vector> #include <boost/pool/pool.hpp> #include <boost/pool/object_pool.hpp> using namespace std; using namespace boost; const int MAXLENGTH = 100000; int main ( ) { boost::pool<> p(sizeof(int)); int* vec1[MAXLENGTH]; int* vec2[MAXLENGTH]; clock_t clock_begin = clock(); for (int i = 0; i < MAXLENGTH; ++i) { vec1[i] = static_cast<int*>(p.malloc()); } for (int i = 0; i < MAXLENGTH; ++i) { p.free(vec1[i]); vec1[i] = NULL; } clock_t clock_end = clock(); cout<<"程序運行了 "<<clock_end-clock_begin<<" 個系統時鐘"<<endl; clock_begin = clock(); for (int i = 0; i < MAXLENGTH; ++i) { vec2[i] = new int; } for (int i = 0; i < MAXLENGTH; ++i) { delete vec2[i]; vec2[i] = NULL; } clock_end = clock(); cout<<"程序運行了 "<<clock_end-clock_begin<<" 個系統時鐘"<<endl; return 0; }
測試環境:VS2008,WindowXP SP2,Pentium 4 CPU雙核,1.5GB內存。
結論:在連續申請和連續釋放10萬塊內存的狀況下,使用內存池耗時是使用new耗時的47.46%。
代碼以下:
#include "stdafx.h" #include <iostream> #include <ctime> #include <vector> #include <boost/pool/pool.hpp> #include <boost/pool/object_pool.hpp> using namespace std; using namespace boost; const int MAXLENGTH = 500000; int main ( ) { boost::pool<> p(sizeof(int)); clock_t clock_begin = clock(); for (int i = 0; i < MAXLENGTH; ++i) { int * t = static_cast<int*>(p.malloc()); p.free(t); } clock_t clock_end = clock(); cout<<"程序運行了 "<<clock_end-clock_begin<<" 個系統時鐘"<<endl; clock_begin = clock(); for (int i = 0; i < MAXLENGTH; ++i) { int* t = new int; delete t; } clock_end = clock(); cout<<"程序運行了 "<<clock_end-clock_begin<<" 個系統時鐘"<<endl; return 0; }
測試結果以下:
結論:在反覆申請和釋放50萬次內存的狀況下,使用內存池耗時是使用new耗時的64.34%。
C++對象在動態申請和釋放時,不只要進行內存操做,同時還要調用構造和析購函數。所以有必要對C++對象也進行內存池的測試。
代碼以下:
#include "stdafx.h" #include <iostream> #include <ctime> #include <vector> #include <boost/pool/pool.hpp> #include <boost/pool/object_pool.hpp> using namespace std; using namespace boost; const int MAXLENGTH = 500000; class A { public: A() { m_i++; } ~A( ) { m_i--; } private: int m_i; }; int main ( ) { object_pool<A> q; clock_t clock_begin = clock(); for (int i = 0; i < MAXLENGTH; ++i) { A* a = q.construct(); q.destroy(a); } clock_t clock_end = clock(); cout<<"程序運行了 "<<clock_end-clock_begin<<" 個系統時鐘"<<endl; clock_begin = clock(); for (int i = 0; i < MAXLENGTH; ++i) { A* a = new A; delete a; } clock_end = clock(); cout<<"程序運行了 "<<clock_end-clock_begin<<" 個系統時鐘"<<endl; return 0; }
測試結果以下:
結論:在反覆申請和釋放50萬個C++對象的狀況下,使用內存池耗時是使用new耗時的112.03%。這是由於內存池的construct和destroy函數增長了函數調用次數的緣由。這種狀況下使用內存池並不能得到性能上的優化。
Boost內存池按照不一樣的理念分爲四類。主要是兩種理念的不一樣形成了這樣的分類。
一是Object Usage和Singleton Usage的不一樣。
Singleton Usage意味着每一個內存池都是一個被靜態分配的對象,直至程序結束纔會被銷燬,這也意味着這樣的內存池是多線程安全的。只有使用release_memory或者 purge_memory方法才能釋放內存。
二是內存溢出的處理方式。第一種方式是返回NULL表明內存池溢出了;第二種方式是拋出異常表明內存池溢出。
根據以上的理念,boost的內存池分爲四種。
Pool是一個Object Usage的內存池,溢出時返回NULL。
object_pool與pool相似,惟一的區別是當其分配的內存釋放時,它會嘗試調用該對象的析購函數。
singleton_pool是一個Singleton Usage的內存池,溢出時返回NULL。
pool_alloc是一個Singleton Usage的內存池,溢出時拋出異常。
#include "stdafx.h" #include <iostream> #include <ctime> #include <vector> #include <boost/pool/pool.hpp> #include <boost/pool/object_pool.hpp> using namespace std; using namespace boost; int _tmain(int argc, _TCHAR* argv[]) { clock_t clock_begin = clock(); int iLength = 0; for (int i = 0; ;++i) { void* p = malloc(1024*1024); if (p == NULL) { break; } ++iLength; } clock_t clock_end = clock(); cout<<"共申請了"<<iLength<<"M內存,程序運行了"<<clock_end-clock_begin<<" 個系統時鐘"<<endl; return 0; }
運行的結果是「共申請了1916M內存,程序運行了 69421 個系統時鐘」,意思是在分配了1916M內存後,malloc已經不可以申請到1M大小的內存塊了。
內存池在底層也是調用了malloc函數,所以內存池也是必然會溢出的。並且內存池可能會比直接調用malloc更早的溢出,看看下面的代碼:
#include "stdafx.h" #include <iostream> #include <ctime> #include <vector> #include <boost/pool/pool.hpp> #include <boost/pool/object_pool.hpp> using namespace std; using namespace boost; int _tmain(int argc, _TCHAR* argv[]) { boost::pool<> pl(1024*1024); clock_t clock_begin = clock(); int iLength = 0; for (int i = 0; ;++i) { void* p = pl.malloc(); if (p == NULL) { break; } ++iLength; } clock_t clock_end = clock(); cout<<"共申請了"<<iLength<<"M內存,程序運行了"<<clock_end-clock_begin<<" 個系統時鐘"<<endl; return 0; }
運行的結果是「共申請了992M內存,程序運行了 1265 個系統時鐘」,意思是在分配了992M內存後,內存池已經不可以申請到1M大小的內存塊了。
從上面的兩個測試能夠看出內存池要比malloc溢出早,個人機器內存是1.5G,malloc分配了1916M才溢出(顯然分配了虛擬內存),而內存池只分配了992M就溢出了。第二點是內存池溢出快,只用了1265微秒就溢出了,而malloc用了69421微秒才溢出。
這些差異是內存池的處理機制形成的,內存池對於內存分配的算法以下,以pool內存池爲例:
因爲每次都是兩倍於原內存,所以當內存池大小達到了992M時,再一次申請就須要1984M,可是malloc最多隻能申請到1916M,所以malloc失敗,內存池溢出。
經過以上原理也能夠理解爲何內存池溢出比malloc溢出要快得多,由於它是以2的指數級來擴大內存池,真正調用malloc的次數約等於log2(1916),而malloc是實實在在進行了1916次調用。因此內存池只用了1秒多就溢出了,而malloc用了69秒。
#include "stdafx.h" #include <iostream> #include <ctime> #include <vector> #include <boost/pool/pool.hpp> #include <boost/pool/object_pool.hpp> using namespace std; using namespace boost; int _tmain(int argc, _TCHAR* argv[]) { boost::pool<> pl(1024*1024); clock_t clock_begin = clock(); int iLength = 0; for (int i = 0; ;++i) { void* p = pl.malloc(); if (p == NULL) { break; } ++iLength; } clock_t clock_end = clock(); cout<<"共申請了"<<iLength<<"M內存,程序運行了"<<clock_end-clock_begin<<" 個系統時鐘"<<endl; clock_begin = clock(); iLength = 0; boost::pool<> pl2(1024*1024); for (int i = 0; ;++i) { void* p = pl2.malloc(); if (p == NULL) { break; } ++iLength; } clock_end = clock(); cout<<"又申請了"<<iLength<<"M內存,程序運行了"<<clock_end-clock_begin<<" 個系統時鐘"<<endl; return 0; }
運行結果以下:
結果代表在第一個內存池溢出後,第二個內存池又提供了480M的內存。
若是不管如何都不能再申請到新的內存了,那麼仍是老老實實告訴用戶重啓程序吧。