昨天晚上,我徒弟跑过来讲,他的程序的内存占用居高不下,愿意是std::map的clear()没有效果。于是我让他用erase(begin,end); 试试也不行。
代码如下:
void release_map(void) { map<int,string> testmap; for(int i=0; i<1000000; i++) { testmap.insert(make_pair(i,"abc")); } testmap.clear(); } int main() { release_map(); while(1) { sleep(1); } return 0; }
用命令 top -p `ps -ef | grep abc | grep -v grep | awk {'print $2'}`, 一查看,占了104M物理内存。
开始我猜测是stl用了自己的缓冲池,clear()并没有归还给系统。于是我用了boost::unordered_map试试,一查看,占了78M物理内存(看来hashmap比红黑树既快又省空间)。
于是上网查询资料,stl有很多种allocator,默认采用是的new_allocator,并没有使用内存缓冲池,针对不同的应用场合,STL中实现了不同的Allocator。
__gnu_cxx::new_allocator<T> Simply wraps ::operator new and ::operator delete.
__gnu_cxx::malloc_allocator<T> Simply wraps malloc and free. There is also a hook for an out-of-memory handler
__gnu_cxx::debug_allocator<T> A wrapper around an arbitrary allocator A. It passes on slightly increased size requests to A, and uses the extra memory to store size information.
__gnu_cxx::__pool_alloc<bool, int> A high-performance, single pool allocator. The reusable memory is shared among identical instantiations of this type.
__gnu_cxx::__mt_alloc<T> A high-performance fixed-size allocatorthat was initially developed specifically to suit the needs of multi threaded applications
__gnu_cxx::bitmap_allocato A high-performance allocator that uses a bit-map to keep track of the used and unused memory locations
发现stl提供的malloc.h有监控功能,于是修改为下面代码:
#include <iostream> #include <string> #include <map> #include <boost/unordered_map.hpp> #include <malloc.h> using namespace std; using namespace boost; void release_map(void) { malloc_stats(); map<int,string> testmap; sleep(2); for(int i=0; i<1000000; i++) { testmap.insert(make_pair(i,"abc")); } malloc_stats(); testmap.clear(); malloc_stats(); } int main() { release_map(); getchar(); return 0; }
发现clear() 其实已经归还内存了,内存的持有是 system bytes 。显然,malloc并没有把这些内存归还给系统,而是缓存起来了。所以说,这个例子的罪魁祸首并不是STL,而是glibc的malloc。好吧,既然找到问题,那就要解决它,虽然glibc的缓存也是一番好意,但是由于实际运行环境不能等到什么用户heap空间内连续空闲内存数据超出一个阈值时才将这片内存归还给内核。
glibc管理内存目前采用的是ptmalloc2,我测试了google的tcmalloc和Jason Evans的jemalloc。
测试很简单,把包downlaod下来并解压,./configure && make && make install即可。
export $LD_PRELOAD="/usr/local/lib/libtcmalloc.so” 或者 export $LD_PRELOAD="/usr/local/lib/libjemalloc.so” (这个要根据自己的实际情况选择路径)
然后编译后可以用ldd查看程序的依赖库。
测试结果:tcmalloc也不归还给系统,而jemalloc的clear后不再占用物理内存。
徒弟问了一句jemalloc靠谱么,我想想淘宝的Tengine,facebook的folly,redis,firefox,freebsd都是用这个,应该是很靠谱的。你上线去测试看看。
附上一张内存分配性能比较图片:
参考文献:
http://blog.163.com/dengminwen@126/blog/static/870226720097189486788/
http://bbs.chinaunix.net/thread-2195511-1-1.html
http://wangkaisino.blog.163.com/blog/static/1870444202011431112323846/