在对应用程序进行编程时,动态内存分配是一个福音。 它有助于在程序运行时分配所需的内存,而不是在进程开始时分配。 但是,有效管理此内存非常重要。 在大型,复杂的应用程序中,内存泄漏是一个非常普遍的问题。 当不再需要或不可访问但仍未释放先前分配的内存时,就会发生内存泄漏,从而导致该进程缺少总可用内存。 虽然良好的编程习惯可以确保将泄漏降到最低,但我们的经验是,当相同的内存块被众多功能处理时,内存泄漏往往会发生。 遇到错误路径时尤其如此。
本文概述了解决内存泄漏时可以使用的方法,并讨论了如何利用AIX®随附的MALLOCDEBUG工具。
为什么内存泄漏是很难解决的问题
您可以根据应用程序遇到的条件在其生命周期的不同时间释放一块内存。 必须对应用程序进行设计,以便它知道何时准备释放一块内存,并尽早执行该操作,而不是稍后执行。 根据控制流,可以在代码中的不同点释放内存。 覆盖所有此类控制流,而不仅仅是命中率最高的代码流路径,这一点非常重要。
同时,在准备释放内存之前释放内存可能是致命的。 因此,对应用程序内的控制流有很好的了解对于理解准备释放块的位置非常有用。
检测内存泄漏
在以下情况下,某些症状表明可能存在泄漏:
- Malloc返回
errno
设置为ENOMEM
。 - 发生核心转储,并且堆栈中的功能之一来自malloc子系统。
- 与正常运行时相比,核心时的进程大小很大。
- 进程大小(可以通过
ps
命令看到)Swift增长。
如果有迹象表明您有内存泄漏,则最好使用内存监视工具。 有一些第三方工具,例如Zerofault和IBMRational®Purify®,但是本文讨论了AIX随附的MALLOCDEBUG工具。
使用MALLOCDEBUG报告内存泄漏
MALLOCDEBUG是易于使用的malloc子系统监视工具。 您可以使用两个环境变量MALLOCDEBUG和MALLOCTYPE轻松地为特定应用程序的malloc子系统启用调试功能。 您需要将这些变量导出到将启动应用程序的外壳程序中。 错误报告将写入AIX版本5.3中的stderr。
合金型
MALLOCTYPE变量确定应使用哪个malloc子系统,以及是否启用调试。 下表1显示了可以设置MALLOCTYPE的各种值。 请参阅相关信息的链接,在“开发和移植的C和C ++在AIX上的应用程序。” IBM红皮书。
表1. MALLOCTYPE变量
MALLOCTYPE = | 内存分配器 | IBM红皮书“在AIX上开发和移植C和C ++应用程序”中的详细解释 |
---|---|---|
3.1 | 3.1内存分配器 | 部分4.2.1,3.1内存分配器 |
空值 | 默认内存分配器 | 部分4.2.2,默认的内存分配器 |
水桶 | 具有malloc存储桶扩展名的默认内存分配器 | 部分4.2.3,带有malloc buckets扩展名的默认内存分配器 |
调试 | 调试内存分配器 | 部分4.2.4,调试内存分配器 |
用户:archive_name | 用户定义的内存分配器 | 部分4.2.5,用户定义的malloc替换 |
您应该知道,MALLOCDEBUG假定使用默认分配器。
MALLOCDEBUG
MALLOCDEBUG变量可以从下表2中的列表中获取多个逗号分隔的值。
表2. MALLOCDEBUG变量
MALLOCDEBUG = | 简要说明 |
---|---|
对齐:n | n是内存对齐的字节数。 请参阅相关主题以获取更多信息。 |
postfree_checking | postfree_checking使MALLOCDEBUG报告尝试访问释放的内存,然后中止程序的尝试。 |
validate_ptrs | 如果将以前未由malloc分配的指针传递给free() 调用,则会引发错误消息,并且程序将中止。 |
overlay_signal_handling | 当MALLOCDEBUG检测到内存错误时,它将使用SIGSEGV或SIGABRT强制程序进入内核。 设置override_signal_handling 可以确保程序终止并运行,即使这些信号已由应用程序处理。 |
allow_overreading | allow_overreading会忽略过度读取。 |
report_allocations | report_allocations报告所有在应用程序退出时未释放的内存分配。 |
record_allocations | record_allocations报告所有内存分配。 |
继续 | 继续会报告错误,但不会终止程序。 这是AIX 5.3版中可用的有用功能。 |
stack_depth:n | stack_depth:n将错误堆栈的深度报告为n ,而不是默认值6。 |
在这些选项中, report_allocations
选项与使用最相关,因为它为您提供了在应用程序中检测到的泄漏的列表。
例
在本节中,您将研究一个示例和一些示例代码清单,以说明如何使用MALLOCDEBUG。 此示例的源代码可在“ 下载”部分中找到。
清单1. MALLOCDEBUG和MALLOCTYPE设置导出到外壳
/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
环境变量导出到外壳程序,则该修复程序将生效。
清单4.释放分配的块后的输出
/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子系统监视工具之一。 为了避免在机器上获取服务时要求客户提供MALLOCDEBUG日志时出现大量malloc错误,在将应用程序交付给客户之前使用MALLOCDEBUG测试应用程序可能非常有益。 我们希望本文为必须处理客户计算机上的内存泄漏的开发人员和服务工程师提供了有用的介绍。
翻译自: https://www.ibm.com/developerworks/aix/library/au-mallocdebug.html