内存池在底层也是调用了malloc函数,因此内存池也是必然会溢出的。而且内存池可能会比直接调用malloc更早的溢出,看看下面的代码:
#include"stdafx.h"#include#include#include#include#includeusingnamespacestd;
usingnamespaceboost;
int_tmain(intargc, _TCHAR*argv[])
...{
boost::pool<>pl(1024*1024);
clock_t clock_begin=clock();
intiLength=0;
for(inti=0; ;++i)
...{
void*p=pl.malloc();
if(p==NULL)
...{
break;
}++iLength;
} clock_t clock_end=clock();
cout<
return0;
}
运行的结果是“共申请了992M内存,程序运行了 1265 个系统时钟”,意思是在分配了992M内存后,内存池已经不能够申请到1M大小的内存块了。
5.2 内存池的基本原理
从上面的两个测试可以看出内存池要比malloc溢出早,我的机器内存是1.5G,malloc分配了1916M才溢出(显然分配了虚拟��存),而内存池只分配了992M就溢出了。第二点是内存池溢出快,只用了1265微秒就溢出了,而malloc用了69421微秒才溢出。
这些差别是内存池的处理机制造成的,内存池对于内存分配的算法如下,以pool内存池为例:
1. pool初始化时带有一个块大小的参数memSize,那么pool刚开始会申请一大块内存,例如其大小为32*memSize。当然它还会申请一些空间用以管理链表,为方便述说,这里忽略这些内存。
2. 用户不停的申请大小为memSize的内存,终于超过了内存池的大小,于是内存池启动重分配机制;
3. 重分配机制会再申请一块大小为原内存池大小两倍的内存(那么第一次会申请64*memSize),然后将这块内存加到内存池的管理链表末尾;
4. 用户继续申请内存,终于又一次超过了内存池的大小,于是又一次启动重分配机制,直至重分配时无法申请到新的内存块。
5. 由于每次都是两倍于原内存,因此当内存池大小达到了992M时,再一次申请就需要1984M,但是malloc最多只能申请到1916M,因此malloc失败,内存池溢出。
通过以上原理也可以理解为什么内存池溢出比malloc溢出要快得多,因为它是以2的指数级来扩大内存池,真正调用malloc的次数约等于log2(1916),而malloc是实实在在进行了1916次调用。所以内存池只用了1秒多就溢出了,而malloc用了69秒。
5.3 内存池溢出的解决方法
对于malloc造成的内存溢出,一般来说没有太多办法可想。基本上就是报一个异常或者错误,然后让用户关闭程序。当然有的程序会有内存自我管理功能,可以让用户选择关闭一切次要功能来维持主要功能的继续运行。
而对于内存池的溢出,还是可以想一些办法的,因为毕竟系统内存还有潜力可挖。
第一个方法是尽量延缓内存池的溢出,做法是在程序启动时就尽量申请最大的内存池,如果在程序运行很久后再申请,可能OS因为内存碎片增多而不能提供最大的内存池。其方法是在程序启动时就不停的申请内存直到内存池溢出,然后清空内存池并开始正常工作。由于内存池并不会自动减小,所以这样可以一直维持内存池保持最大状态。
第二个方法是在内存池溢出时使用第二个内存池,由于第二个内存池可以继续申请较小块的内存,所以程序可继续运行。代码如下:
#include"stdafx.h"#include#include#include#include#includeusingnamespacestd;
usingnamespaceboost;
int_tmain(intargc, _TCHAR*argv[])
...{
boost::pool<>pl(1024*1024);
clock_t clock_begin=clock();
intiLength=0;
for(inti=0; ;++i)
...{
void*p=pl.malloc();
if(p==NULL)
...{
break;
}++iLength;
} clock_t clock_end=clock();
cout<
clock_begin=clock();
iLength=0;
boost::pool<>pl2(1024*1024);
for(inti=0; ;++i)
...{
void*p=pl2.malloc();
if(p==NULL)
...{
break;
}++iLength;
} clock_end=clock();
cout<
return0;
}
运行结果如下:
结果表明在第一个内存池溢出后,第二个内存池又提供了480M的内存。
5.4 内存池溢出的终极方案
如果无论如何都不能再申请到新的内存了,那么还是老老实实告诉用户重启程序吧。