内存泄漏检测总结

本文来自http://blog.csdn.net/lijun84 ,引用必须注明出处!

 

在谈及内存泄漏时,对于没有太多经验的新人来说总是很头疼的一件事。因为如果项目早期没有将其纳入代码框架,后期部署上线之后,仅从进程 crash dump 很难找到线索,即使有最后的调用栈信息也很难下手。

 

本文只想谈谈在各种情况下如何检测内存泄漏,也算给自己这方面的经验做个小结。

 

貌似谈内存泄漏检测目的有些多余。无非就是定位到泄漏点,然后是失误就修改,是自己的内存管理框架问题,那么就考虑改机制。

但仔细想想什么地方才可能是泄漏点,其实这个问题依情况而定还是比较复杂的。因为造成泄漏无非就是分配了但没有释放,可是你不了解软件的架构或结构,你如何知道正确的处理流程是该什么时候释放,在哪释放?

所以通常内存检测会分为 2 个步骤,第一个步骤通常可以工具辅助自动化,而第二个步骤也许必须人工做。

(1)    检测到大量分配而未释放以造成内存大量消耗或耗尽的代码点

(2)    根据代码结构和实现方式发现应该在哪释放,如何释放 (有可能需要改变代码结构)

 

内存泄漏的分类:

(1)       常发性内存泄漏。发生内存泄漏的代码会被多次执行到,每次被执行的时候都会导致一块内存泄漏。

 

(2)       偶发性内存泄漏。发生内存泄漏的代码只有在某些特定环境或操作过程下才会发生。

 

(3)       一次性内存泄漏。发生内存泄漏的代码只会被执行一次,或者由于算法上的缺陷,导致总会有一块仅且一块内存发生泄漏。

 

(4)       隐式内存泄漏。程序在运行过程中不停的分配内存,但是直到结束的时候才释放内存。

 

内存泄漏的检测形式和阶段:

(1)       架构期纳入:这种情况一般会有自己的内存管理模块,并且有各模块分配 / 释放的追踪机制,且在日志中体现。

优点:

(a)       使内存管理的问题清晰化,更易掌控和全盘布局。

(b)       能尽早发现内存问题。

缺点:

(a)       当项目时间比较紧时,需要花费额外 efforts

(b)       追踪机制的代码本身编写要相当谨慎,不能发现泄漏问题。

 

(2)       项目提交前:此阶段可以用一些侵入式检测工具,这类工具代价也比较小,一般加个头文件或加个编译选项什么的就搞定。

优点:代价小,不会影响项目进度,且通常情况下比较有效。

缺点:在些特定情况下不那么有效(如:项目中做了自己的内存管理框架并有一定程度的封装)

 

(3)       项目提交并部署后的运行期:这个阶段基本就是亡羊补牢了。也是最头痛的,救火阶段。此阶段一般限制很多(如:无法替换客户程序并且还原客户部署环境,更甚者连客户端的相应版本代码已没了),那么这时基本只能寄希望于一些动态非侵入式的检测工具了。

      优点:很幸运,你可能有有效的检测工具可用。

缺点:很不幸,可能此类工具限制比较多,甚至无法使用(多数情况如此,因为现在的软件环境已经很庞大很复杂了)。

 

内存泄漏检测方法分类:

(1)       替换接口函数法:

这种方法的思想比较简单,并且大量侵入式的检测工具也是这样做的(其实一些 CRT 都有自带,还有些自带的 malloc 调试库)。替换或重载你的 malloc/new free/delete 函数,并做下面几件事:

(a)    在你成功申请内存后:记录你的当前函数名和代码行和你申请的内存块的地址。

(b)    将以此函数名(模块)下的内存块计数器加 1.

(c)    在申请释放内存前:对比所有函数名(模块)下的已分配内存块地址,找到就将相应计数器减 1.

当然实现思想大致如此,可真正实现是需要一些优化的,比如将函数名或内存地址做 hash ,以便省去搜索消耗。

 

(2)       内存快照对比法:

这种方法不常用,并且效果不太好,但对于以最快的方式发现内存在什么阶段存在泄漏还是比较有效的。

 

(3)       运行期 Hook 法:

这种方法在很多商业工具中常用,单地说,当你的程序开始运行时,它的 DLL 被自动载入进程的地址空间(这可以通过 system-level Hook 实现),然后它会修改进程中对内存分配和释放的函数调用,让这些调用首先转入它的代码,然后再执行原来的代码。实际只是方法 1 的变种。

 

(4)       进程资源监控法:

此方法用于取代方法 2 快速判断是否存在内存泄漏问题时比较有效,可以通过系统自带的 Performance Monitor ,查看内存相关计数的值来快速判断是否存在泄漏。

 

内存泄漏检测工具:

(1)       静态扫描工具: splint, BEAM 等,此类工具可以检测没初始化的变量,废弃的空指针,内存泄漏,冗余计算等潜在问题。但缺点是误报太多。就和编译器的警告一样,你可以重视,但它不一定是引发问题的根源。

 

(2)       动态侵入性工具: mtrace dmalloc memwatch VLD N 多,都是利用堆栈快照 + TAG 的方法。

 

(3)       动态非侵入性工具: BoundsChecker purify, valgrind, YAMD 等,一般用 Hook 运行库的方法。

 

(4)       资源监控工具: windows 上一般就是 Performance Monitor, linux 可以用 ps 带相应的参数。

 

小结:

虽然上面介绍了那么多种方法,其实内存泄漏发生时往往现场情况更棘手,资源和信息的缺乏还是给解决问题带来了很大难度。

我个人建议如果团队都是新手,没什么和内存打交道经验的话,尽量避免用 C/C++ ,而 . NET Java 也许成本会低很多,维护代价也会小很多。

当然如果种种原因(如客户要求)不得不用,那么最好能考虑用些成熟的 GC 库,如 HP Boehm’s GC.

最后如果客户连第三方库的使用也限制了,那么你在项目早期就要重视内存管理和追踪的问题。

       嘿嘿,如果以上都失效了。也到了最极端的地步,没办法,考验你实力的时候到了,只有 Crack 级调试的经验可以帮助你了。

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值