遭遇内存泄露实例

前方传来消息,某个过程三十次后死机,看log,内存分配失败。很明显只有两种可能,内存泄露或者内存碎片。经确认此过程仅单个模块申请内存,所以内存碎片基本可以排除了,因为哪怕申请过程内存比较混乱,内存支离破碎,如果没有内存泄露,那最终也是会释放干净而不存在碎片的。

打开内存申请释放函数中的统计信息,果然一个重复以后,内存占用增加了6K多。这个项目本身内存很吃紧,这将近200K的内存造成死机也就很正常。

模块本身相对庞大,一行一行的去查内存泄露明显不可能,也没那个耐心。于是顺理成章的拿内存申请释放函数开刀,一个链表,存储每次申请的信息,内存就用每次申请时多申请的部分,于是泄露的内存很快现出原形,一个0x68字节的,若干个0x5a字节的。

继续定位,问题出现了,CSND《大内高手》看到过,用行号和文件名就能定位到申请的位置。可这个位置固定在内存申请函数中,要定位到调用的位置,难道需要顺着栈追上去?最后我的解决方案是在内存申请函数中检查,遇到0x68或者0x5a大小的内存申请就直接返回NULL,通过调用处的断言定位。(第一次感觉到申请内存后断言一下多么重要啊,hoho)方法虽笨点,不过总是还是把内存泄露降低到了5K。

再下去,新麻烦又来了,0x5a的申请有多处,而最初的很多次都没有泄露。于是在链表中增加了遇到0x5a的index一项,记录这是第几次遇0x5a。这样从新的报告中就知道了泄露的0x5a的index,所幸这个过程重复性比较好,index每次基本上都不变,根据index返回NULL,问题终告解决。

在本文完成后,在网上找到了更好的办法。上面的的方案如果过程的重复性不好,那index就没有任何意义了。总的来说就是使用如下的定义,详细过程可参考《一个跨平台的 C++ 内存泄漏检测器》。

void *   operator   new (size_t size,  const   char *  file,  int  line); 
void *   operator   new [](size_t size,  const   char *  file,  int  line); 

#define  new DEBUG_NEW 
#define  DEBUG_NEW new(__FILE__, __LINE__)

感谢absurd(http://blog.csdn.net/absurd/)《大内高手--调试手段及原理》一文,感谢Eric(http://blog.csdn.net/wwwsq/)提供吴咏炜的《一个跨平台的 C++ 内存泄漏检测器》(http://www-128.ibm.com/developerworks/cn/linux/l-mleak2/index.html)一文,当然更要感谢吴咏炜本人。

补充一点:《一个跨平台的 C++ 内存泄漏检测器》使用自定义自定义全局"operator new(size_t)"的办法防止部分未引用头文件(既部分地方调用了不带文件名和行号的new)的情况下,delete出错的问题。(系统的new没有处理我们加的数据节点)

再补充一点,其实也是一直有些想不明白的,实现我们自己的new的cpp文件,不能包含有new宏的文件,否则自身会被替换混乱,另外直接#define new new(__FILE__, __LINE__)测试似乎也可以,不需要中转。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值