|
<noscript></noscript>
<noscript><trvalign><td width="8"><img alt="" height="1" width="8" src="//www.ibm.com/i/c.gif"></td>
<td width="16"><img alt="" width="16" height="16" src="//www.ibm.com/i/c.gif"></td>
<td class="small" width="122"><p><span class="ast">未显示需要 JavaScript的文档选项</span></p></td></trvalign></noscript>
|
<script language="javascript" type="text/javascript"><!--document.write('<tr valign="top"><td width="8"><img src="//www.ibm.com/i/c.gif" width="8" height="1" alt=""/></td><td width="16"><img alt="将打印机的版面设置成横向打印模式" height="16" src="//www.ibm.com/i/v14/icons/printer.gif" width="16" vspace="3" /></td><td width="122"><p><b><a class="smallplainlink" href="javascript:print()">打印本页</a></b></p></td></tr>');//--></script>
|
|
|
<script language="javascript" type="text/javascript"><!--document.write('<tr valign="top"><td width="8"><img src="//www.ibm.com/i/c.gif" width="8" height="1" alt=""/></td><td width="16"><img src="//www.ibm.com/i/v14/icons/em.gif" height="16" width="16" vspace="3" alt="将此页作为电子邮件发送" /></td><td width="122"><p><a class="smallplainlink" href="javascript:document.email.submit();"><b>将此页作为电子邮件发送</b></a></p></td></tr>');//--></script>
|
|
|
<!--start reserved for future use include files--><!-- this content will be automatically generated across all content areas --><!--end reserved for future use include files--> |
别: 初级
韩 兆兵 (hanzb@cn.ibm.com), 软件工程师, IBM 刘 盈 (cdlliuy@cn.ibm.com), 软件工程师, IBM 强 晟 (qiangsh@cn.ibm.com), 软件工程师, IBM
2008 年 5 月 15 日
由于 C 和 C++程序中完全由程序员自主申请和释放内存,稍不注意,就会在系统中导入内存错误。同时,内存错误往往非常严重,一般会带来诸如系统崩溃,内存耗尽这样严重的后果。本文将从静态分析和动态检测两个角度介绍在 Linux 环境进行内存泄漏检测的方法,并重点介绍静态分析工具 BEAM、动态监测工具Valgrind 和 rational purify 的使用方法。相信通过本文的介绍,能给大家对处理其它产品或项目内存泄漏相关的问题时提供借鉴。
<!--start reserved for future use include files--><!-- include java script once we verify teams wants to use this and it will work on dbcs and cyrillic characters --><!--end reserved for future use include files--> 由于 C 和C++程序中完全由程序员自主申请和释放内存,稍不注意,就会在系统中导入内存错误。同时,内存错误往往非常严重,一般会带来诸如系统崩溃,内存耗尽这样严重的后果。从历史上看,来自计算机应急响应小组和供应商的许多最严重的安全公告都是由简单的内存错误造成的。自从 70 年代末期以来,C/C++程序员就一直讨论此类错误,但其影响在 2007年仍然很大。与许多其他类型的常见错误不同,内存错误通常具有隐蔽性,即它们很难再现,症状通常不能在相应的源代码中找到。例如,无论何时何地发生内存泄漏,都可能表现为应用程序完全无法接受,同时内存泄漏不是显而易见[1]。存在内存错误的 C 和 C++程序会导致各种问题。如果它们泄漏内存,则运行速度会逐渐变慢,并最终停止运行;如果覆盖内存,则会变得非常脆弱,很容易受到恶意用户的攻击。 因此,出于这些原因,需要特别关注 C 和 C++编程的内存问题,特别是内存泄漏。本文先从如何发现内存泄漏,然后是用不同的方法和工具定位内存泄漏,最后对这些工具进行了比较,另外还简单介绍了资源泄漏的处理(以句柄泄漏为例)。本文使用的测试平台是:Linux (Redhat AS4)。但是这些方法和工具许多都不只是局限于 C/C++语言以及 linux 操作系统。 内存泄漏一般指的是堆内存的泄漏。堆内存是指程序从堆中分配的、大小任意的(内存块的大小可以在程序运行期决定)、使用完后必须显示的释放的内存。应用程序一般使用malloc、realloc、new等函数从堆中分配到一块内存,使用完后,程序必须负责相应的调用 free 或 delete释放该内存块。否则,这块内存就不能被再次使用,我们就说这块内存泄漏了。 1. 如何发现内存泄漏 有些简单的内存泄漏问题可以从在代码的检查阶段确定。还有些泄漏比较严重的,即在很短的时间内导致程序或系统崩溃,或者系统报告没有足够内存,也比较容易发现。最困难的就是泄漏比较缓慢,需要观测几天、几周甚至几个月才能看到明显异常现象。那么如何在比较短的时间内检测出有没有潜在的内存泄漏问题呢?实际上不同的系统都带有内存监视工具,我们可以从监视工具收集一段时间内的堆栈内存信息,观测增长趋势,来确定是否有内存泄漏。在 Linux 平台可以用ps 命令,来监视内存的使用,比如下面的命令 (观测指定进程的VSZ值):
2. 静态分析 包括手动检测和静态工具分析,这是代价最小的调试方法。 2.1 手动检测 当使用 C/C++进行开发时,采用良好的一致的编程规范是防止内存问题第一道也是最重要的措施。检测是编码标准的补充。二者各有裨益,但结合使用效果特别好。专业的 C或 C++ 专业人员甚至可以浏览不熟悉的源代码,并以极低的成本检测内存问题。通过少量的实践和适当的文本搜索,您能够快速验证平衡的*alloc() 和 free() 或者 new 和 delete 的源主体。人工查看此类内容通常会出现像清单 1 中一样的问题,可以定位出在函数 LeakTest 中的堆变量 Logmsg 没有释放。 清单1. 简单的内存泄漏
#include <stdio.h> #include <string.h> #include <stdlib.h>
int LeakTest(char * Para) { if(NULL==Para){ //local_log("LeakTest Func: empty parameter/n"); return -1; } char * Logmsg = new char[128]; if(NULL == Logmsg){ //local_log("memeory allocation failed/n"); return -2; } sprintf(Logmsg,"LeakTest routine exit: '%s'./n", Para); //local_log(Logmsg); return 0; } int main(int argc,char **argv ) { char szInit [] = "testcase1"; LeakTest(szInit); return 0; }
|
2.2 静态代码分析工具 代码静态扫描和分析的工具比较多,比如 splint, PC-LINT, BEAM 等。因为 BEAM 支持的平台比较多,这以 BEAM 为例,做个简单介绍,其它有类似的处理过程。 BEAM 可以检测四类问题: 没有初始化的变量;废弃的空指针;内存泄漏;冗余计算。而且支持的平台比较多。 BEAM 支持以下平台:
- Linux x86 (glibc 2.2.4)
- Linux s390/s390x (glibc 2.3.3 or higher)
- Linux (PowerPC, USS) (glibc 2.3.2 or higher)
- AIX (4.3.2+)
- Window2000 以上
清单2. 用作 Beam 分析的代码
#include <stdio.h> #include <string.h> #include <stdlib.h>
int *p;
void foo(int a) { int b, c;
b = 0; if(!p) c = 1;
if(c > a) c += p[1]; }
int LeakTest(char * Para) { char * Logmsg = new char[128]; if((Para==NULL)||(Logmsg == NULL)) return -1; sprintf(Logmsg,"LeakTest routine exit: '%s'./n", Para); return 0; }
int main(int argc,char **argv ) { char szInit [] = "testcase1"; LeakTest(szInit); return 0; }
|
下面以 X86 Linux 为例,代码如清单 2,具体的环境如下: OS: Red Hat Enterprise Linux AS release 4 (Nahant Update 2) GCC: gcc version 3.4.4 BEAM: 3.4.2; https://w3.eda.ibm.com/beam/ 可以把 BEAM 看作一个 C/C++ 编译器,按下面的命令进行编译 (前面两个命令是设置编译器环境变量): ./beam-3.4.2/bin/beam_configure --c gcc ./beam-3.4.2/bin/beam_configure --cpp g++ ./beam-3.4.2/bin/beam_compile --beam::compiler=compiler_cpp_config.tcl -cpp code2.cpp
|
从下面的编译报告中,我们可以看到这段程序中有三个错误:”内存泄漏”;“变量未初始化”;“ 空指针操作” "code2.cpp", line 10: warning: variable "b" was set but never used int b, c; ^
BEAM_VERSION=3.4.2 BEAM_ROOT=/home/hanzb/memdetect BEAM_DIRECTORY_WRITE_INNOCENTS= BEAM_DIRECTORY_WRITE_ERRORS=
-- ERROR23(heap_memory) /*memory leak*/ >>>ERROR23_LeakTest_7b00071dc5cbb458 "code2.cpp", line 24: memory leak ONE POSSIBLE PATH LEADING TO THE ERROR: "code2.cpp", line 22: allocating using `operator new[]' (this memory will not be freed) "code2.cpp", line 22: assigning into `Logmsg' "code2.cpp", line 24: deallocating `Logmsg' because exiting its scope (losing last pointer to the memory)
-- ERROR1 /*uninitialized*/ >>>ERROR1_foo_60c7889b2b608 "code2.cpp", line 16: uninitialized `c' ONE POSSIBLE PATH LEADING TO THE ERROR: "code2.cpp", line 10: allocating `c' "code2.cpp", line 13: the if-condition is false "code2.cpp", line 16: getting the value of `c'
VALUES AT THE END OF THE PATH: p != 0
-- ERROR2 /*operating on NULL*/ >>>ERROR2_foo_af57809a2b615 "code2.cpp", line 17: invalid operation involving NULL pointer ONE POSSIBLE PATH LEADING TO THE ERROR: "code2.cpp", line 13: the if-condition is true (used as evidence that error is possible) "code2.cpp", line 16: the if-condition is true "code2.cpp", line 17: invalid operation `[]' involving NULL pointer `p'
VALUES AT THE END OF THE PATH: c = 1 p = 0 a <= 0
|
2.3 内嵌程序 可以重载内存分配和释放函数 new 和 delete,然后编写程序定期统计内存的分配和释放,从中找出可能的内存泄漏。或者调用系统函数定期监视程序堆的大小,关键要确定堆的增长是泄漏而不是合理的内存使用。这类方法比较复杂,在这就不给出详细例子了。
3. 动态运行检测 实时检测工具主要有 valgrind, Rational purify 等。 3.1 Valgrind valgrind 是帮助程序员寻找程序里的 bug 和改进程序性能的工具。程序通过 valgrind 运行时,valgrind 收集各种有用的信息,通过这些信息可以找到程序中潜在的 bug 和性能瓶颈。 Valgrind现在提供多个工具,其中最重要的是 Memcheck,Cachegrind,Massif 和 Callgrind。Valgrind 是在Linux 系统下开发应用程序时用于调试内存问题的工具。它尤其擅长发现内存管理的问题,它可以检查程序运行时的内存泄漏问题。其中的memecheck 工具可以用来寻找 c、c++ 程序中内存管理的错误。可以检查出下列几种内存操作上的错误:
- 读写已经释放的内存
- 读写内存块越界(从前或者从后)
- 使用还未初始化的变量
- 将无意义的参数传递给系统调用
- 内存泄漏
3.2 Rational purify RationalPurify主要针对软件开发过程中难于发现的内存错误、运行时错误。在软件开发过程中自动地发现错误,准确地定位错误,提供完备的错误信息,从而减少了调试时间。同时也是市场上唯一支持多种平台的类似工具,并且可以和很多主流开发工具集成。Purify 可以检查应用的每一个模块,甚至可以查出复杂的多线程或进程应用中的错误。另外不仅可以检查 C/C++,还可以对 Java 或 .NET 中的内存泄漏问题给出报告。 在 Linux 系统中,使用 Purify 需要重新编译程序。通常的做法是修改 Makefile 中的编译器变量。下面是用来编译本文中程序的 Makefile:
首先运行 Purify 安装目录下的 purifyplus_setup.sh 来设置环境变量,然后运行 make 重新编译程序。
下面给出编译一个代码文件的示例,源代码文件命名为 test3.cpp. 用 purify 和 g++ 的编译命令如下,‘-g’是编译时加上调试信息。 purify g++ -g test3.cpp –o test
|
运行编译生成的可执行文件 test,就可以得到图1,可以定位出内存泄漏的具体位置。
清单3. Purify 分析的代码
#include <unistd.h> char * Logmsg;
int LeakTest(char * Para) { if(NULL==Para){ //local_log("LeakTest Func: empty parameter/n"); return -1; } Logmsg = new char[128]; for (int i = 0 ; i < 128; i++) Logmsg[i] = i%64;
if(NULL == Logmsg){ //local_log("memeory allocation failed/n"); return -2; } sprintf(Logmsg,"LeakTest routine exit: '%s'./n", Para); //local_log(Logmsg); return 0; }
int main(int argc,char **argv ) { char szInit [] = "testcase1"; int i; LeakTest(szInit); for (i=0; i < 2; i++){ if(i%200 == 0) LeakTest(szInit); sleep(1); } return 0; }
|
需要指出的是,程序必须编译成调试版本才可以定位到具体哪行代码发生了内存泄漏。即在 gcc 或者 g++ 中,必须使用 "-g" 选项。 图 1 purify 的输出结果
结论 本文介绍了多种内存泄漏,定位方法(包括静态分析,动态实时检测)。涉及到了多个工具,详细描述的它们的用法、用途以及优缺点。对处理其它产品或项目内存泄漏相关的问题有很好的借鉴意义。
参考资料
学习
获得产品和技术
- 订购 SEK for Linux,共包含两张 DVD,其中有用于 Linux 的最新 IBM 试用软件,包括 DB2®、 Lotus®、Rational®、Tivoli® 和 WebSphere®。
- 用可直接从 developerWorks 下载的 IBM 试用软件 构建您的下一个 Linux 开发项目。
讨论
作者简介
|
|
|
韩兆兵,IBM 中国软件开发中心工程师,在 WVS and EVV 组工作,从事 Websphere VoiceServer 的技术支持工作。 |
|
|
|
刘盈,IBM 软件开发中心工程师,在 WVS and EVV 组工作,从事 Websphere VoiceServer 的技术支持工作。 |
|
|
|
强晟,IBM 中国软件开发中心工程师,在 WVS and EVV 组工作,从事 Websphere VoiceServer 的技术支持工作。 |
|
相关推荐
在 Linux 平台中调试 C-C++ 内存泄漏方法
在 windows 下使用 VC 编程时,我们通常需要 DEBUG 模式下运行程序,而后调试器将在退出程序时,打印出程序运行过程中在堆上分配而没有释放的内存信息,其中包括代码文件名、行号以及内存大小。该功能是 MFC ...
在Linux平台上有valgrind可以非常方便的帮助我们定位内存泄漏,因为Linux在开发领域的使用场景大多是跑服务器,再加上它的开源属性,相对而言,处理问题容易形成“统一”的标准。而在Windows平台,服务器和客户端...
在Linux平台上有valgrind可以非常方便的帮助我们定位内存泄漏,因为Linux在开发领域的使用场景大多是跑服务器,再加上它的开源属性,相对而言,处理问题容易形成“统一”的标准。而在Windows平台,服务器和客户端...
在 windows 下使用 VC 编程时,我们通常需要 DEBUG 模式下运行程序,而后调试器将在退出程序时,打印出程序运行过程中在堆上分配而没有释放的内存信息,其中包括代码文件名、行号以及内存大小。该功能是 MFC ...
Valgrind 是一款 Linux下(支持 x86、x86_64和ppc32)程序的内存调试工具,它可以对编译后的二进制程序进行内存使用监测(C语言中的malloc和free,以及C++中的new和delete),找出内存泄漏问题。
一、C++、C内存池、内存泄漏调试的实现 二、Windows、Linux双平台线程池的实现. 三、一些标准编码的封装类实现. _base64Encode.h _urlEncode.h _utf8Encode.h base32Encode.cpp base32Encode.h base64Encode.cpp ...
在很多脚本语言中,您不必担心内存是如何管理的,这并不能使得内存管理的重要性有一点点降低。对实际编程来说,理解您的内存管理器的能力与局限性至关重要。在大部分系统语言中,比如 C 和 C++,您必须进行内存...
在大部分系统语言中,比如 C 和 C++,您必须进行内存管理。本文将介绍手工的、半手工的以及自动的内存管理实践的基本概念。 追溯到在 Apple II 上进行汇编语言编程的时代,那时内存管理还不是个大问题。您实际上在...
Valgrind 是一款 Linux下(支持 x86、x86_64和ppc32)程序的内存调试工具,它可以对编译后的二进制程序进行内存使用监测(C语言中的malloc和free,以及C++中的new和delete),找出内存泄漏问题。
用C/C++开发其中最令人头疼的一个问题就是内存管理,有时候为了查找一个内存泄漏或者一个内存访问越界,需要要花上好几天时间,如果有一款工具能够帮助我们做这件事情就好了,valgrind正好就是这样的一款工具。...
此为内存泄露检测工具,用于调试和分析Linux程序,主要针对C和C++。
你可以在它的环境中运行你的程序来监视内存的使用情况,比如C 语言中的malloc和free或者 C++中的new和 delete。使用Valgrind的工具包,你可以自动的检测许多内存管理和线程的bug,避免花费太多的时间在bug寻找上,...
代码里用了备份dll的方法,因此在自定义的函数中可以直接调用在内存中备份的dll代码,而不需要再把函数头部改来改去。 IOCP反弹远控客户端模型,外加上线服务端,全部代码注释! 如题。这个是IOCP远程控制软件的...
您在Linux / Windows内核中工作的stl !!! 备注:不适用于KCL_MEMORY_DEBUG,因为如果要使用内置的功能强大的内存泄漏工具,调试类std :: map尚未完成,则将map的实现添加到文件kernel_memory_map.h中。 例如,您...
代码里用了备份dll的方法,因此在自定义的函数中可以直接调用在内存中备份的dll代码,而不需要再把函数头部改来改去。 IOCP反弹远控客户端模型,外加上线服务端,全部代码注释! 如题。这个是IOCP远程控制软件的...
CPPNotes 如下是 C++ 后台研发技术路线以及知识点,这里有很多细节,还需要不断完善。 欢迎大家通过 或者加我 与我交流,一起成长一起进步! 以下没有连接的内容就是还没有写,笔者正在拼命...内存泄露检查工具Valgrind
本教程共分为5个部分,第一部分是C语言提高部分,第二部分为C++基础部分,第三部分为C++进阶部分,第四部分为C、C++及数据结构基础部分,第五部分为C_C++与设计模式基础,内容非常详细. 第一部分 C语言提高部分目录...
3. 增加memory.c内存管理和debug.c调试信息及日志记录。 4. ccpaging建议改掉qqqun这个结构名,我打算下个版本把它改为qun_t,其它类似如 member_t, group_t, buddy_t。 5. 具备登录输入验证码功能,验证码图片保存...
它足够可靠,可以在自动化中用于在泄漏发生之前捕获泄漏。 作为一种交互式工具,它可以帮助解释内存的增长,可以识别某些形式的损坏,并通过给出各种内存位置的状态来补充调试器。 目前, chap仅支持glibc malloc...