检测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规格描述将解释器移植到特定的计算机上,就能保证经过编译的 ...
随机推荐
- 使用JAVA开发微信公众平台(一)——环境搭建与开发接入
一. 初始微信公众平台 微信公众平台,即我们平时所说的"公众号",曾用名"官方平台"."媒体平台",但最终命名为"公众平台&quo ...
- 前端基本知识(一):W3C标准&&冒泡事件,捕获事件,W3C DOM对象模型,对比分析
W3C标准是万维网联盟, 其他的可以参考万维网版本的更新内容 一.W3C标准 二.W3C DOM事件 三.冒泡事件 四.捕获事件 一.W3C标准 其实网页是由三分部组成:1.结构(structure) ...
- javaweb log4j显示完整sql日志
javaweb显示完整sql日志 所需jar包: log4j-1.2.17.jar log4jdbc-1.2.jar slf4j-api-1.7.12.jar slf4j-log4j12-1.7.12 ...
- Jenkins的插件
Jenkins不仅自己为大家提供了很多功能,而且还支持插件.用户可以根据自己的需要安装插件,或者是自己开发插件. 这里说一下Jenkins的插件的安装方法: 首先,打开Jenkins首页,选择Jenk ...
- (C#)xml的简单理解创建和读取
xml知识点清理:一.文档规则 1.区分大小写. 2.属性值必须加引号(单引号.双引号都可以),一般情况下建议使用使用双引号. 3.所有标记必须有结束符号. 4.所有空标记必须关闭. 5.必须有且仅有 ...
- memcached学习总结
一.介绍1.基于libevent的事件处理 libevent是一套跨平台的事件处理接口的封装,能够兼容包括这些操作系统:Windows/Linux/BSD/Solaris 等操作系统的的事件处理.包装 ...
- 连连看的原生JS实现
那天闲来无事,便想找个小游戏来打发时间,后来便找到了连连看, 玩了一会儿感觉无聊,想到各位高手用JS做的各种小游戏,便想自己也来做一个,于是便有了这几天的成果. 代码是用 原生JS 实现的,只是用来学 ...
- HTTP基础知识(二)
接着上一章的内容:HTTP基础知识(一) 二.简单的HTTP协议 1.客户端:请求访问文本或图像等资源的一端称为客户端: 服务器端:提供资源响应的一端 2.以百度为例子 这是请求头: 在起始行 ...
- 网络性能测试工具Iperf/Jperf解读
Iperf 是一个网络性能测试工具.Iperf 可以测试TCP 和UDP 带宽质量.Iperf 可以测量最大TCP 带宽,具有多种参数和UDP 特性. Iperf 可以报告带宽,延时抖动和数据包丢失. ...
- Redis 安装与初体验
一.Redis简介 Redis是一个key-value存储系统.和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list(链表).set(集合).zset(s ...