替代系统自带的malloc/new原因无非两个:
reason 1. 做内存profile或查找问题
reason 2. 自定义的分配方案提高性能
不过文章[1]中说明了,替代全局new不是一个好做法. 其实要达到以上两点目的,笔者认为用valgrind工具链就可以了。
解决方案:
1. 用valgrind和massif
valgrind的memcheck做内存泄露和bug的查找, 里面的massif工具包做内存性能profile, 足矣。比自己山寨的一个profiler要好。
注意:tcmalloc目前还不能很好支持valgrind, 实测中jemalloc可以
2. linux下C的程序可以用wrap的方式(相当于python的decorator)
编译加上选项:gcc -Wl,-wrap,malloc
可以做到对malloc这个函数,linker会调用__wrap_malloc代替之, 若要调用原来的malloc函数__real_malloc
缺点:依赖于编译器支持; 对c++的new不起作用 --> 不实用
启示:这个方法作为function装饰器,对于调试别的问题倒有帮助。(例如不改变函数的情况下,wrap一层,输出些调试信息)
3. 用__malloc_hook
参考: http://linux.die.net/man/3/__malloc_hook
#include <malloc.h>
void *(*__malloc_hook)(size_t size, const void *caller);
缺点:依赖GNU编译工具链; 容易死循环(想利用原有malloc,要参考例子中,把原__malloc_hook变量保存起来使用,并恢复现场)
4. LD_PRELOAD注入.so ,替代原
环境变量LD_PRELOAD指定程序运行时优先加载的动态连接库,这个动态链接库中的符号优先级是最高的。标准C的各种函数都是存放在libc.so.6的文件中,在程序运行时自动链接。使用LD_PRELOAD后,自己编写的malloc的加载顺序高于glibc中的malloc,这样就实现了替换。用法 LD_PRELOAD=" ./mymalloc.so"
缺点:在生产环境不现实。因为LD_PRELOAD相当于库注入,有安全性问题,是必须禁止的。(生产环境很多时候用-static连接)
5. 用宏或另外的函数替代new/malloc
比如定义一个宏或者指定的函数,规定所有的分配释放都调用他。这样相当于给项目引入了额外的代码规则(而且是一立项就要遵循这个规则,否则该方法无效),不能很自然的new/delete, 如果分配和释放调用得不一致,会产生问题的。某产品组就是用宏,然后加上__FILE__, __LINE__之类的信息。
有时候valgrind的效率是个问题(尤其生产环境),这种方案有其价值所在, 就是代码看上去比较ugly罢了
用宏的例子:
#define _New(Type, Catergory) (Type*)MyMemController::New((new Type), #Type, 1, sizeof(Type), Catergory, __FILE__, __LINE__, false)
#define _NewArray(Type, N, Catergory) (Type*)MyMemController::New((new Type[N]), #Type, N, sizeof(Type)*(N), Catergory, __FILE__, __LINE__, true)
MALLOC的替代品:
自己写一个malloc其实很复杂,要考虑线程安全等各种问题,性能到头来可能更差。google 的tcmalloc, facebook使用的jemalloc. 多线程下性能较好,可以考虑使用。
缺点:笔者尝试过。tcmalloc不能正确用valgrind,只能用自带gperftools(运行中会core)
jemalloc可以使用valgrind,不过还没完全验证是否都准确。
tcmalloc相关:
在64位系统上要装libunwind, 对x86-64架构使用还有些问题
源码包的INSTALL文档里面也提到了这个问题。
CAUTION: if you install libunwind from the url above, be aware that
you may have trouble if you try to statically link your binary with
perftools: that is, if you link with 'gcc -static -lgcc_eh ...'.
This is because both libunwind and libgcc implement the same C++
exception handling APIs, but they implement them differently on
some platforms. This is not likely to be a problem on ia64, but
may be on x86-64.
主要是64位机frame-pointer的影响, 他的profile工具里的backtrace用libunwind这个库,这个库又有版本问题,各种囧啊....
笔者试过系统x86-64, freebsd,用静态链接。实际用了一下,问题很多很折腾,等他fix了再说吧.
windows下可以参考:
http://www.cppblog.com/feixuwu/archive/2010/07/10/119980.aspx
jemalloc暂时未发现有什么兼容性问题,运行得挺好的。
Reference
[1] <不要重载全局operator new>
[2] effective c++条款50:了解new和delete的合理替换时机
[3] 游戏引擎中的内存分配策略
[4] 更好的内存管理jemalloc
[5] tcmalloc官网(gperftools)