Heath's Blog

There is no end, it is just the beginning! - A Game Developer's Notes

Windows平台下针对C/C++项目的内存泄漏检测方法

     由于语言在自动内存管理上的欠缺,C/C++在内存管理上从来都是需要程序员小心处理的一个方面,当项目代码上了一定规模,内存消耗和泄漏就会成为程序稳定运行的第一大敌。如果不在项目之初就建立内存管理和泄漏检测机制,后面蛋疼的问题就会接踵而来。这篇文章着重讨论内存泄漏检测,而内存管理与具体项目类型关系密切,后面有时间我会着重游戏项目来讨论。

     内存泄漏检测的基本步骤是:1)包装(重载)内存分配/释放API;2)进行内存分配时记下相关信息:地址、大小、调用栈;3)释放时清除之前记录的对应信息;4)程序退出时(确保在所有内存释放操作完成之后),输出剩下的记录。其中,对进行分配操作是的调用栈回溯是个重点信息,它能够帮助我们找出内存泄漏代码。

     Windows中的Dbghelp库提供了丰富的调试API。StackWalk应该是进行栈回溯最直接的一种接口了,但是它不够快。如果能先记录下调用栈上的CALL指令地址,然后在输出日志时解析出符号,将会大大降低检测机制对程序本身性能的影响。Dbghelp库中提供了Sym*FromAddr系列API,可以通过指令地址获取函数符号,那么剩下的就是如何记录指令地址的问题了。从网上借了一张x86调用栈示意图,如下:

Image(12)

     从图中可以看出,Callee的EBP始终指向Caller的EBP,EBP下面是指向Caller下一条指令(注意x86体系下栈的增长方向是小地址),因此通过EBP就可以回溯整个调用栈了。通过下面代码可以实现此功能:

Image(13)

     宏参数frame是个void*指针数组,数组的大小取决于想要回溯的栈深度。内存分配和回收的包装代码如下:

Image(14)

     我们看到,内存管理系统内部终究还是要使用语言提供的内存分配/释放API,只要配对实现了分配与释放管理,系统内部的无泄漏是很容易保证的。在这里着重讲解原理,就不重载new/delete operator了。最后看一下调用栈函数符号的回溯代码:

Image(15)

     我们用下面代码做测试用例:

Image(16)

     泄漏检测结果:

Image(17)

参考:

[1] Using DbgHelp

[2] Intel x86 Function-call Conventions - Assembly View

posted on 2013-10-26 08:55 Heath 阅读(4305) 评论(4)  编辑 收藏 引用 所属分类: Game Development

Feedback

# re: Windows平台下针对C/C++项目的内存泄漏检测方法 2013-10-26 09:39 战魂小筑

windows下VLD搞定, 还开源, 都不用研究了  回复  更多评论   

# re: Windows平台下针对C/C++项目的内存泄漏检测方法[未登录] 2013-10-28 10:26 Heath

@战魂小筑
VLD这么好用,但为什么很多游戏引擎都会自己写一套内存管理和检测机制呢?  回复  更多评论   

# re: Windows平台下针对C/C++项目的内存泄漏检测方法 2013-10-28 20:59 孤独剑客zzy

这个非常有用,可以简单的实现valgrind了,必须顶!!!  回复  更多评论   

# re: Windows平台下针对C/C++项目的内存泄漏检测方法 2013-11-20 21:12 GLimix

这方面的文章不多,好文。  回复  更多评论   


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