在 AIX V5.3 中使用 MALLOCDEBUG 隔离并解决内存泄漏

用于完成烦琐任务的有用工具

AIX® Version 5.3 中附带的 malloc 子系统监视工具 MALLOCDEBUG,可以帮助您隔离内存泄漏。内存泄漏是非常棘手的问题,并且处理起来需要很大的代价,所以使用好的工具以确定并了解出现泄漏的原因是非常重要的。本文中提供的示例代码将向您展示处理内存泄漏的一种方法。

Manish Katiyar, 软件工程师, IBM

Manish Katiyar 是 IBM India Software Labs 的软件工程师。在过去两年里,他一直为 IBM 工作,主要研究 SARPC,并具有数据仓库工具 (Ab-Initio) 的丰富使用经验。Manish 拥有印度 Indian Institute of Technology Kharagpur 大学的化学工程学士学位。您可以通过 manish.katiyar@in.ibm.com 与他联系。



Vaarun Vijairaghavan (vivaarun@in.ibm.com), 系统软件工程师, IBM

Vaarun Vijairaghavan 是印度 Pune 的 IBM India Software Labs 的系统软件工程师。在过去三年里,他从事分布式计算环境 (DCE) 项目的开发。他的研究兴趣包括网络安全、容错技术和分布式计算。他拥有 Pune 大学计算机工程学士学位。您可以通过 vivaarun@in.ibm.com 与他联系。



2007 年 5 月 10 日

引言

在编写应用程序时进行动态内存分配是非常必要的。它可以在程序运行的过程中帮助分配所需的内存,而不是在进程启动的时候就进行分配。然而,有效地管理这些内存同样也是非常重要的。在大型的、复杂的应用程序中,内存泄漏是很常见的问题。当以前分配的一片内存不再需要使用或无法访问时,但是却并没有释放它,那么对于该进程来说,会因此导致总的可用内存的减少,这时就出现了内存泄漏。尽管优秀的编程实践可以确保最少的泄漏,但是根据经验,当使用大量的函数对相同的内存块进行处理时,很可能会出现内存泄漏。尤其是在碰到错误路径的情况下更是如此。

本文介绍了一种内存泄漏处理方法,并讨论了如何利用 AIX® 中附带的 MALLOCDEBUG 工具。

为什么内存泄漏难以解决

应用程序可以根据所碰到的具体情况,在其生命周期中的不同时刻释放一片内存。编程人员需要对应用程序进行合理的设计,以便它能够知道何时可以释放一块内存,并马上执行这个操作,而不是等待更长的时间。根据具体的控制流程,可以在代码中的不同地方进行内存的释放。有一点至关重要,那就是必须涉及到所有的控制流程,而不仅仅是最常执行的代码流程路径。

同时,在内存做好释放准备之前对其进行释放可能引起灾难性的错误。因此,要了解内存块在何处做好了释放的准备,必须全面地了解应用程序中的控制流程。

检测内存泄漏

当出现下列症状时,表示可能出现了泄漏:

  • malloc 返回的 errno 被设置为 ENOMEM
  • 发生核心转储,并且堆栈中的一个函数来自于 malloc 子系统。
  • 在进行核心转储时,进程的大小比正常运行时要大。
  • 进程大小(可以使用 ps 命令来查看)急剧增长。

如果出现了一些内存泄漏的症状,那么最好使用内存监视工具进行分析。有许多第三方的工具,如 Zerofault 和 IBM Rational® Purify®,但本文将讨论 AIX 中附带的 MALLOCDEBUG 工具。

使用 MALLOCDEBUG 报告内存泄漏

MALLOCDEBUG 是一种很易于使用的 malloc 子系统监视工具。通过使用两个环境变量 MALLOCDEBUG 和 MALLOCTYPE,您可以轻松地对特定的应用程序启用 malloc 子系统调试任务。您需要将这些变量导出到运行该应用程序的 Shell 中。在 AIX Version 5.3 中,错误报告将写入到 stderr。

MALLOCTYPE

MALLOCTYPE 变量用来确定应该使用何种 malloc 子系统,以及是否启用调试任务。下面的表 1 显示了 MALLOCTYPE 各种可能的取值。请参见参考资料部分,其中提供了“Developing and Porting C and C++ Applications on AIX”的链接。IBM 红皮书。

表 1. MALLOCTYPE 变量
MALLOCTYPE=内存分配器IBM 红皮书“Developing and Porting C and C++ Applications on AIX”中的详细解释
3.13.1 内存分配器 4.2.1 节,3.1 内存分配器
NULL缺省内存分配器 4.2.2 节,缺省内存分配器
buckets具有 malloc 桶扩展的缺省内存分配器 4.2.3 节,具有 malloc 桶扩展的缺省内存分配器
debug调试内存分配器 4.2.4 节,调试内存分配器
user:archive_name用户定义的内存分配器 4.2.5 节,用户定义的内存分配器

您应该清楚,MALLOCDEBUG 假定使用的是缺省分配器。

MALLOCDEBUG

MALLOCDEBUG 变量可以包含下面表 2 的列表中的多个值,这些值之间以逗号分隔。

表 2. MALLOCDEBUG 变量
MALLOCDEBUG=简要解释
Align:nn 是内存对齐的数值。关于更多的信息,请参见参考资料部分。
postfree_checkingpostfree_checking 使得 MALLOCDEBUG 报告对已释放的内存进行访问的尝试,然后中止该程序。
validate_ptrs如果向 free() 调用传递的指针并不是以前由 malloc 分配的,那么将会引发一个错误消息,而该程序将被中止。
override_signal_handling当 MALLOCDEBUG 检测到内存错误,它会强迫该程序使用 SIGSEGV 或 SIGABRT 进行核心转储。设置 override_signal_handling 可以确保该程序终止并进行核心转储,即使应用程序已经对这些信号进行了处理。
allow_overreading allow_overreading 将忽略读取越界。
report_allocations report_allocations 将报告在应用程序退出时所有未释放的内存分配。
record_allocations record_allocations 将报告所有的内存分配。
continue继续报告错误,但它并不异常终止 (ABEND) 该程序。这是 AIX Version 5.3 中提供的一种非常有用的特性。
stack_depth:nstack_depth:n 将错误堆栈报告到第 n 层,而不是缺省的第 6 层。

在这些选项中,使用 report_allocations 最为合适,因为它可以为您提供已检测到的应用程序中泄漏的列表。

示例

在这一部分中,您将看到一个示例和一些示例代码清单,用以说明如何使用 MALLOCDEBUG。下载部分中提供了这个示例的源代码。

清单 1. 将 MALLOCDEBUG 和 MALLOCTYPE 设置导出到 Shell
/home/user> export MALLOCTYPE=debug
/home/user> export MALLOCDEBUG=report_allocations
清单 2. 程序的输出
/home/user> ./stud 2>mal_dbg_stud
Student Name: Manish
 Subjects # Maths   English

Student Name: Vaarun
 Subjects # Chemistry   Physics

Student Name: Sandeep
 Subjects # Biology   Maths

	......
	......
	......
	......

Student Name: Govind
 Subjects # Physics   Biology

Student Name: Manish
 Subjects # Maths   English

Current allocation report:

    Allocation #0: 0x2000EFE8
        Allocation size: 0x14
        Allocation traceback:
        0xD0371F64  malloc
        0xD0322194  init_malloc
        0xD0323224  malloc
        0x10000BE4  initialize_leaky

    Allocation #1: 0x20010FF0
        Allocation size: 0xA
        Allocation traceback:
        0xD0371F64  malloc
        0x10000C08  initialize_leaky
        0x10000DD4  main
        0x100001B4  __start

    Allocation #2: 0x20012FF0
        Allocation size: 0xA
        Allocation traceback:
        0xD0371F64  malloc
        0x10000C08  initialize_leaky
        0x10000DD4  main
        0x100001B4  __start
	......
	......
	......
	......
    Allocation #17: 0x20030FF0
        Allocation size: 0x10
        Allocation traceback:
        0xD0371F64  malloc
        0x100008F4  getnode
        0x10000B08  insert
        0x10000DDC  main

    Allocation #18: 0x20032FF8
        Allocation size: 0x8
        Allocation traceback:
        0xD0371F64  malloc
        0x10000894  allocate_for_dbl_ptr
        0x10000910  getnode
        0x10000B08  insert

    Allocation #19: 0x20034FE8
        Allocation size: 0x14
        Allocation traceback:
        0xD0371F64  malloc
        0x10000A5C  allocate_name
        0x10000B18  insert
        0x10000DDC  main

    Allocation #22: 0x2003AFF0
        Allocation size: 0x10
        Allocation traceback:
        0xD0371F64  malloc
        0x100008F4  getnode
        0x10000B3C  insert
        0x10000DDC  main
	......
	......
	......
	......

MALLOCDEBUG 的输出格式对于用户来说并不是很友好。它只是为每个已分配且尚未释放的内存块打印出调用 malloc 的函数堆栈。它还返回了内存块的大小以及由 malloc 所返回的指针值。

为了使上面的输出更容易理解,我们编写了一个示例脚本。(请参见下载部分。请注意,这是一个适用于与该示例程序具有类似输出的示例脚本。)下面的清单 3 显示了在对上面这些堆栈运行了该脚本之后的输出。

清单 3. 使用脚本解析 MALLOCDEBUG 的输出
/home/user> ./format_mallocdebug_op.ksh
Usage : ./stack.ksh stackfile

/home/katiyar/DW> ./format_mallocdebug_op.ksh ./mal_dbg_stud | 
   tee ./mal_dbg_stud_formatted
Parsing output from debug malloc ...
Analyzed 50 stacks ...

main
insert
allocate_subjects
malloc
################################
220 bytes leaked in 22 Blocks
################################
main
insert
allocate_name
malloc
################################
220 bytes leaked in 11 Blocks
################################
__start
main
initialize_leaky
malloc
################################
190 bytes leaked in 16 Blocks
################################
main
insert
getnode
malloc
################################
176 bytes leaked in 11 Blocks
################################
insert
getnode
allocate_for_dbl_ptr
malloc
################################
88 bytes leaked in 11 Blocks
################################
initialize_leaky
malloc
init_malloc
malloc
################################
20 bytes leaked in 1 Blocks
################################

调查工作应从最大的泄漏着手。上面的第一个堆栈显示出,main 调用了 insert,后者调用了 allocate_subjects。而 allocate_subjects 通过调用 malloc 来分配内存块,并且这块内存一直没有释放。该脚本的输出显示,22 次独立的分配总共产生了 220 个字节的泄漏。这表示您应该查看代码,找出在 allocate_subjects 函数中分配 10 个字节的数据的位置,并检查是否释放了其所有的实例。通过对该程序的代码进行仔细的分析,您可以找到出现问题并且应该释放的内存块。

可以对报告中的每个堆栈进行类似的分析,并对其进行修复。如果在运行该程序之前将 FREE_BLOCKS=1 环境变量导出到了 Shell,那么对这个程序进行的修复将会立即生效。

清单 4. 释放了通过 malloc 分配的内存块之后的输出
/home/katiyar/DW> ./stud
Student Name: Manish
 Subjects # Maths   English

Student Name: Vaarun
 Subjects # Chemistry   Physics

Student Name: Sandeep
 Subjects # Biology   Maths

	......
	......
	......
	......

Student Name: Govind
 Subjects # Physics   Biology

Student Name: Manish
 Subjects # Maths   English

Current allocation report:

Total allocations: 0.

结束语

当您的软件运行在客户的计算机上时,MALLOCDEBUG 是可供使用的 malloc 子系统监视工具之一。当用户在计算机上使用软件的过程中出现 malloc 错误时,需要用户提供 MALLOCDEBUG 日志,为了避免这种情况,在将应用程序提交给客户之前,使用 MALLOCDEBUG 对其进行测试是很有帮助的。我们希望本文能够为那些必须处理客户计算机中的内存泄漏问题的开发人员和服务工程师提供有用的简介。


下载

描述名字大小
Sample program and parsing script for debug mallocau-articles-progs.zip4KB

参考资料

学习

获得产品和技术

  • IBM 试用软件:使用 IBM 软件开发您的下一个项目,可直接从 developerWorks 下载这些试用软件。

讨论

条评论

developerWorks: 登录

标有星(*)号的字段是必填字段。


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件

 


在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。

所有提交的信息确保安全。

选择您的昵称



当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

标有星(*)号的字段是必填字段。

(昵称长度在 3 至 31 个字符之间)

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

 


所有提交的信息确保安全。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=AIX and UNIX
ArticleID=219097
ArticleTitle=在 AIX V5.3 中使用 MALLOCDEBUG 隔离并解决内存泄漏
publish-date=05102007