检测Windows程序的内存和资源泄漏之原生语言环境
最近接连收到大客户的反馈,我们开发的一个软件,姑且称之为App-E吧,在项目规模特别大的情况下,长时间使用会逐渐耗尽内存,运行越来越缓慢,软件最终崩溃。由于App-E是使用混合语言开发的,主界面使用C#, 核心模块库大多是采用C/C++实现的DLL,很多配置和用户流程又是用Tcl/Tk脚本语言写的, 普通的内存检测工具比如Purify,Purify Plus, Valgrind 要么不支持Windows,要么不支持混合语言,对于这种复杂架构下的内存问题都无能为力。无奈之下,只好采取分而治之的方式,对不同的代码模块采用不同的检测分析方法。我打算在这篇博客里把分析原生语言内存和资源泄漏过程中尝试过的工具和使用经验做一下初步的总结,下篇文章打算翻译一篇MSDN上关于检测和避免.Net环境下的内存和资源泄漏的佳作。
原生语言的内存泄漏检测
使用微软debug版本的C Run-Time库自带的内存泄漏检测APIs
Microsoft的这篇开发文档:Memory Detection Enabling详细介绍了如何开启内存检测。理论上这种方法很简单,只需要在程序头文件中包含以下三行代码,并在Debug模式下重新编译代码,链接debug版本的C Run-Time 库即可。
#define _CRTDBG_MAP_ALLOC
#include<stdlib.h>
#include<crtdbg.h>
但我们的产品恰恰很难满足这两个基本条件,原因是:把产品App-E切换到debug版本的C Run-Time库很费劲,频繁做全编译的代价了也太大了。 很多产品即使在debug模式下也习惯使用release版本的运行库以提高运行速度,我们的产品长久以来就是如此。我试着在Makefile中把Run-Time库从msvcrt.dll切换为msvcprtd.dll,手工编译了几个小模块发现没有问题,include上述头文件也没有问题;但是启动全编译后第二天早晨检查编译结果,发现产生了好几百个编译错误,要知道产品App-E全编译一遍需要8个小时左右,反反复复调试编译问题我根本耗不起。
使用免费的第三方内存泄漏检测库Visual Leak Detector
- 优点
类似于微软的方案,VLD也要求在待分析程序的源代码中包含VLD的头文件并重新编译程序;相对于微软的方案,VLD的最大优点是不需要依赖debug版本的Run-Time库,并且支持C和C++语言,还可以更方便地定制内存检测报告。 - 缺点
即使只想检测某个模块库中的问题,也不能仅仅只在特定库对应的源代码中包括VLD头文件,VLD要求必须在主程序入口处编译链接VLD头文件。这意味着我还是不能避免全编译。此外,VLD也不支持混合代码模式,如果主程序是用C#等托管语言开发的,即使算法库或功能模块库使用native语言开发,VLD也无能为力,我们的产品App-E正是如此。
开发一段简单的内存泄漏检测代码,按需编译
经过几天的分析对比,我已经很确定内存泄漏问题只存在于特定的几个算法库和他们对应的用户界面,那么怎么才能短平快地针对这几个特定的库做内存泄漏分析呢? 内存泄漏检测的机制很简单,自己写段代码实现检测方法,在特定库中编译链接使用岂不是更方便?事实上早就有人这么做了,我不需要重新发明轮子。codeproject有人分享了一段C语言的内存检测代码,基本思想如下:
- 使用宏定义把malloc/free方法映射到自己定义的my_malloc/my_free方法上
- 在my_malloc方法中调用malloc方法分配内存,在一个哈希表中记录内存指针,分配的内存大小和代码调用文件和行数等信息;
C语言的库函数对集合的支持很差,没有直接可用的哈希表,可以自己实现一个链表代替。
主流的C语言编译器都支持__FILE__/__LINE__宏定义用以获取代码调用文件和行数,我们只需要定义一个合适的struct来保存内存分配信息就行; - 在my_free方法中调用free方法释放内存,清除对应的内存分配记录。
- 实现一个report方法,遍历记录内存分配的链表,输出没被释放的内存块对应的代码调用行数等信息。
- 在待分析的模块库代码的合适位置调用report方法,重新编译该模块库。
资源泄漏检测
使用上述codeproject的内存检测代码,对几个特定算法库进行了检测,我发现它们的内存泄漏并不严重,那么为什么程序消耗的内存一直在快速增长,运行速度也越来越慢呢? 考虑到算法模块的用户交互界面是用tcl/tk实现的,我怀疑也许tcl/tk脚本存在诸如GDI对象之类的系统资源泄漏,那有哪些工具可以分析检测资源泄漏呢?
Process Explorer
作为SysInternals工具集的一员,Process Explorer的进程检测和管理能力堪称强大,我平时一直用它替代Windows 任务管理器,最近才发现Process Explorer的Performance Tab页堪称检测进程内存和资源使用情况的利器。

Bear
Bear是一款专注于系统资源检测的Windows小工具,它可以检测:
- 所有GDI对象的使用情况 (hDC, hRegion, hBitmap, hPalette, hFont, hBrush)
- 所以用户对象的使用情况 (hWnd, hMenu, hCursor, SetWindowsHookEx, SetTimer and some other stuff)
- 句柄(Handle)数量
相对于Process Explorer的大而全,Bear专注而细腻,更便于我们监测定位进程具体的资源泄漏类型,从而缩小排查的范围。

结合使用两种工具,对比在进行特定软件操作前后的系统资源变化情况,并对照相应的源代码,我在tk脚本中成功发现了一处资源泄漏,MDI Window上显示的canvas在销毁窗口后没有被destroy,导致在打开关闭含有大量器件的原理图时,每次都会产生2M左右的内存增长,GDI对象更是数以百计地增长。
检测Windows程序的内存和资源泄漏之原生语言环境的更多相关文章
- Java程序在内存中运行详解
目录 Java程序在内存中运行详解 一.JVM的内存分布 二.程序执行的过程 三.只有一个对象时的内存图 四.两个对象使用同一个方法的内存图 五.两个引用指向同一个对象的内存图 六.使用对象类型作为方 ...
- Windows系统中内存泄露与检测工具及方法
1.检测需要使用的工具:windbg工具.检测前,需要先安装windbg工具.安装了该工具后,会在安装目录下有一个umdh工具.假设windbg安装在以下目录下:D:\Program Files\De ...
- eclipse 检测App的内存占用和泄漏【转载】
前段时间开发的Android应用,每次都是在运行了半个小时左右后突然挂掉了,很是莫名其妙,也不知道哪里出了问题,后来一步步排查,发现问题出在JNI层,一个被频繁调用的函数分配的内存忘记释放,导致内存泄 ...
- 使用_CRTDBG_LEAK_CHECK_DF检查VC程序的内存泄漏(转)
我们知道,MFC程序如果检测到存在内存泄漏,退出程序的时候会在调试窗口提醒内存泄漏.例如: class CMyApp : public CWinApp{public:BOOL InitApplicat ...
- [Swift通天遁地]七、数据与安全-(11)如何检测应用程序中的内存泄露
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★➤微信公众号:山青咏芝(shanqingyongzhi)➤博客园地址:山青咏芝(https://www.cnblogs. ...
- 编写高质量代码改善C#程序的157个建议——建议69:应使用finally避免资源泄漏
建议69:应使用finally避免资源泄漏 除非发生让应用程序中断的异常,否则finally总是会先于return执行.finally的这个语言特性决定了资源释放的最佳位置就是在finally块中:另 ...
- Windows 程序启动性能优化(先载入EXE,后载入DLL,只取有限的代码载入内存,将CPU的IP指向程序的入口点)
一.重定位链接时重定位:目标文件一般由多个节组成,编译器在编译每个目标文件时一般都是从0地址开始生成代码.当多个代码节合成一个代码段时,需要根据其在最终代码段中的位置做出调整.同时,链接器需要对已经解 ...
- java\c程序的内存分配
JAVA 文件编译执行与虚拟机(JVM)介绍 Java 虚拟机(JVM)是可运行Java代码的假想计算机.只要根据JVM规格描述将解释器移植到特定的计算机上,就能保证经过编译的任何Java代码能够在该 ...
- java程序的内存分配
java程序的内存分配 JAVA 文件编译执行与虚拟机(JVM)介绍 Java 虚拟机(JVM)是可运行Java代码的假想计算机.只要根据JVM规格描述将解释器移植到特定的计算机上,就能保证经过编译的 ...
随机推荐
- 5种IE hasLayoutt的属性及其值
hasLayout 是Windows Internet Explore 渲染引擎的一个内部组成部分.在 Internet Explore 中,一个元素要么自己对自身内容进行计算大小和组织,要么依赖 ...
- Win10+Ubuntu16.04双系统安装
硬件工具: 一台PC 一个U盘(8GB以上) Win10安装(已经装好Win10的小朋友们请无视): 准备工作: 下载Win10升级助手 保证系统盘有8GB以上剩余空间 安装步骤(由于安装过程中未记录 ...
- 最新升级的火狐38.0.6识别ajax调用返回的""空值可能有异常。
自已在调用一段ajax开发中,返回的是空值 string result = string.Empty;return result; 但在页面进行$.ajax调用 时 输出alert(result);应 ...
- 浅谈stream数据流
汴水流,泗水流,流到瓜州古渡头, 吴山点点愁. 我们知道水是源源不断的, 抽刀断水水更流, 斩不断, 理还乱, 是水流.(技术贴, 本文权当读者没学过古诗). 在一些语言里, 我们的前辈把数据 ...
- 手动加支付宝遇到的错误--iOS
前言 之前调通了支付宝demo,开始往自己工程拖东西吧,我为什么觉得我可能把所以的问题都遇到了呢+_+,赶紧把问题记录下来 不然下次弄还费劲,加一句,要不真的用ping++吧
- 关于hibernate注解的简单应用
@Override 用途:重写父类的同名方法 单元测试注解 @Test 用途:用于测试 @Before 用途:单测方法走之前执行 @After 用途:单测方法走之后执行 注解的目标:替换小配置.替换h ...
- Kosaraju算法解析: 求解图的强连通分量
Kosaraju算法解析: 求解图的强连通分量 欢迎探讨,如有错误敬请指正 如需转载,请注明出处 http://www.cnblogs.com/nullzx/ 1. 定义 连通分量:在无向图中,即为连 ...
- c#基础语句——循环语句(for、while、foreach)
循环类型:for.while.foreach 循环四要素:初始条件-->循环条件-->循环体-->状态改变 1.for 格式: for(初始条件:循环条件:状态改变) {循环体(br ...
- mfc--弹出文件夹对话框
1. HRESULT CoInitializeEx(void* pvReserved,DWORD dwCoInit) 初始化com组件,传递参数COINIT_APARTMENTTHREADED 2.L ...
- iframe----摘抄出处未知
1.frame不能脱离frameSet单独使用,iframe可以: 2.frame不能放在body中:如下可以正常显示: <!--<body>--> <frameset ...