Daly的游戏人生

替代系统malloc/new--选择合适的内存跟踪方案

 
替代系统自带的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下可以参考:

jemalloc暂时未发现有什么兼容性问题,运行得挺好的。
 
Reference
[1] <不要重载全局operator new>

[2] effective c++条款50:了解new和delete的合理替换时机

[3] 游戏引擎中的内存分配策略
[4] 更好的内存管理jemalloc
[5] tcmalloc官网(gperftools)

posted on 2012-07-02 13:01 Daly 阅读(7325) 评论(0)  编辑 收藏 引用 所属分类: C/C++


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理