最近刚换工作,还在试用期,分配给我的也都是些零碎的任务。
前阵子领导扔给我一个小项目,这个项目底层使用C++编写的3D渲染引擎,然后用CLI包装了一下,提供给上层的C#调用。这个项目存在比较严重的内存泄漏问题,由于目前公司写C++的人寥寥无几,写C#的又不太懂如何检测C++的内存泄漏,领导就把这个小任务扔给了我。
本文主要介绍三种内存检测技术:VLD、CRT、Deleaker
1.VLD (Visual Leak Detector)
VLD 介绍
vld是一个免费的、健壮的、开源的内存检测工具,适用于VS2008-2015,使用之前需要先安装,下载地址。下载完之后获得vld-2.5.1-setup.exe,这里我用的是当下最新的2.5.1版本。安装完成之后会在VS的包含目录中默认添加vld的头文件包含路径:
VLD的使用非常简单,只需要在C++项目的任意一个参与编译的cpp文件中引用vld.h头文件
然后在Debug模式下运行程序,当程序退出时,在调试窗口就会打印出产生的内存泄漏
泄漏信息中可以定位到具体的泄漏行,并包括详细的堆栈调用。我用VS2017也试了一下,虽然也能检测到泄漏,但不能定位到具体行,网上有一些所谓的黑科技可以解决,大致就是把VS2017安装目录下的dbghelp.dll覆盖到vld的安装目录下,读者可以自己试一下。
2.CRT(C运行时库)
CRT的使用不需要引用第三方头文件或库,在程序中包含如下代码
然后在需要检测泄漏的地方调用_CrtDumpMemoryLeaks(),Debug模式下运行程序
如果希望在程序退出时自动打印所有的内存泄漏信息,需要在程序启动时调用_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF)
这样在程序退出的时候也会打印如上的泄漏信息,并定位到行号。
但是如果只是加入上面的代码,我们只有使用malloc造成的内存泄漏才能定位到具体行号,但是在C++中大多数情况下我们使用new动态分配内存,为了能够检测到使用new时候的泄漏位置,需要加上如下代码
即对new操作符进行了重定义,这样在程序中我们使用new的时候,debug模式下会用到重定义后的版本
这样即可打印出具体的内存泄漏行号。
3.Deleaker(可以检测C#中的内存泄漏)
官网
Deleaker是收费软件,有14天的试用期,虽然收费,但是确实好用。要下载Deleaker需要先在这个页面填写用户名和邮件信息,然后会给该邮件发送一个下载链接,下载完后获得DeleakerSetup_2018.1.0.0.exe(最新版)。Deleaker有两种模式,一种是作为单独的程序运行,一种是作为VS的插件(对VS支持很友好,支持多种版本2005~2017,支持x86和x64平台)。安装完成后打开VS,在VS顶部会多出一个选项
Deleaker有两种运行模式,托管模式和.Net模式,检测C++的时候使用托管模式,在Deleaker Window中启用Deleaker
程序运行结束会在Window中列出泄漏的位置
由于我的Deleaker已经过了试用期,所有看不到具体的行号...
Deleaker可以设置检测的具体的泄漏类型,Deleaker->Options->Settings->General Settings->Allocation types
Deleaker比较好用的一个功能是快照
可以在程序运行过程中记录当前程序的内存快照,隔一段时间后再次记录快照,然后将这两个快照进行比较,内存泄漏问题就比较容易定位了!
Tips:在使用Deleaker的过程中遇到了一个问题,由于我这个项目是用CLI包装C++后供C#调用,主程序是C#的,调试C#程序时记录快照特别慢...我尝试和Deleaker的开发者进行了沟通,对方也做了些改进,给了我几个改进后的版本,虽然速度有所提高,但还是有些慢,最后也就不了了之了。Deleaker单独测试一个纯C++或者纯C#程序时,快照的速度还是相当满意的。
以上就是三种内存泄漏检测方式,读者可以根据自己的需求选择合适的工具。