目录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的内存。
若是不管如何都不能再申请到新的内存了,那么仍是老老实实告诉用户重启程序吧。