Oracle® Developer Studio 12.5:使用 dbx 调试程序

退出打印视图

更新时间: 2016 年 6 月
 
 

使用内存泄漏检查

内存泄漏是动态分配的内存块,在程序的数据空间中任何位置都没有指向它的指针。这类块是孤立内存。由于任何指针均未指向块,因此程序无法引用这些块,也很少释放它们。运行时检查会查找并报告这类块。

内存泄漏会导致占用的虚拟内存增加,且通常会导致产生内存碎片。这可能会降低程序及整个系统的性能。

通常情况下,导致出现内存泄漏的原因是未释放分配的内存,而又丢失了指向分配块的指针。下面是一些内存泄漏示例:

void
foo()
{
    char *s;
    s = (char *) malloc(32);

    strcpy(s, "hello world");

    return; /* no free of s. Once foo returns, there is no     */
            /* pointer pointing to the malloc’ed block,         */
            /* so that block is leaked.                         */
}

API 使用不当会导致泄漏。

void
printcwd()
{

    printf("cwd = %s\n", getcwd(NULL, MAXPATHLEN));

    return; /* libc function getcwd() returns a pointer to     */
            /* malloc’ed area when the first argument is NULL, */
            /* program should remember to free this. In this   */
            /* case the block is not freed and results in leak.*/
}

总是在不再需要内存时便将其释放,并密切注意返回已分配内存的库函数,便可避免内存泄漏。如果使用这类函数,记得要适当地释放内存。

有时,内存泄漏一词用于指未释放的内存块。此定义用处不大,这是因为如果程序不久后即将终止,则常见的编程惯例是不释放内存。如果程序仍保留一个或多个指向块的指针,则运行时检查不会将该块报告为泄漏。

检测内存泄漏错误

可能的泄漏

运行时检查可以在两种情况下报告“可能的”泄漏。第一种情况是任何指针均未指向块的开头处,但有一个指针指向块的内部。这种情况将报告为 "Address in block" 地址位于块内 (aib) 错误。指向该块的迷失指针是真正的内存泄漏。但是,某些程序会根据需要有意反复移动指向数组的唯一指针来访问其条目。这类情况不是内存泄漏。由于运行时检查无法区分这两种情况,因此会将这两种情况都按可能的泄漏来报告,由您来确定哪一种情况是真正的内存泄漏。

当数据空间中没有找到指向块的指针,但在寄存器中找到指针时,将发生第二种可能的泄漏。这种情况按“地址位于寄存器内 (air)”错误来报告。如果寄存器意外指向内存块或寄存器是后来丢失了的内存指针的旧副本,便是真正的泄漏。不过,编译器可以优化引用以及将指向内存块的唯一指针放入寄存器中,而不必将指针写入内存。这类情况便不是真正的泄漏。因此,如果程序已优化且报告是 showleaks 命令的结果,则这类情况很可能不是真正的泄漏。 所有其他情况便可能是真正的泄漏。有关更多信息,请参见showleaks 命令


注 -  运行时泄漏检查要求使用标准 libc malloc/free/realloc 函数或基于这些函数的分配器。有关其他分配器,请参见运行时检查应用编程接口

检查泄漏

如果启用了内存泄漏检查,则会在所测试的程序即将退出之前,自动执行内存泄漏扫描。 检测到的所有泄漏都会报告出来。不应使用 kill 命令中止程序。 以下示例是一条典型的内存泄漏错误消息:

Memory leak (mel):
Found leaked block of size 6 at address 0x21718
At time of allocation, the call stack was:
    [1] foo() at line 63 in test.c
    [2] main() at line 47 in test.c

UNIX 程序具有 main 过程(在 f77 中称为 MAIN),该过程是程序的顶级用户函数。程序通常以两种方式终止:一种是调用 exit(3),另一种是从 main 返回。在后一种情况下,main 的所有局部变量都会在返回后超出作用域,而它们指向的所有堆块都会按泄漏来报告(除非有全局变量也指向这些块)。

常见的编程惯例是不释放已分配给 main 中局部变量的堆块,这是因为程序即将终止,并在不调用 exit() 的情况下从 main 中返回。要防止运行时检查将这种块按内存泄漏来报告,请在 main 中最后一个可执行源代码行设置一个断点,以在 main 返回前停止程序。当程序在该处停止时,使用 showleaks 命令报告所有真正的泄漏,而忽略仅由 main 中的变量超出作用域导致的泄漏。

有关更多信息,请参见showleaks 命令

理解内存泄漏报告

启用泄漏检查之后,您将在程序退出时收到一份自动泄漏报告。所有可能的泄漏都会报告出来,但前提是程序不是使用 kill 命令中止的。报告中的详细程度由 dbxenv 变量 rtc_mel_at_exit 控制。缺省情况下,将生成非详细的泄漏报告。

报告按泄漏的合并大小排序。先报告真正的内存泄漏,然后报告可能的泄漏。详细报告包含详细的堆栈跟踪信息,其中包括行号和可用的源文件。

两种报告都包括内存泄漏错误的下列信息:

Size(大小)

泄漏块的大小

Location(位置)

泄漏块被分配到的位置

Address(地址)

泄漏块的地址

Stack(堆栈)

分配时调用堆栈,受 check -frames 限制

以下是对应的非详细内存泄漏报告。

Actual leaks report    (actual leaks:    3 total size:    2427 bytes)

 Total      Num of  Leaked      Allocation call stack
 Size       Blocks  Block
                    Address
==========  ====== ==========  =======================================
      1852       2      -      true_leak < true_leak
       575       1    0x22150  true_leak < main

Possible leaks report  (possible leaks:  1  total size:       8 bytes)

 Total      Num of  Leaked      Allocation call stack
 Size       Blocks  Block
                    Address
==========  ====== ==========  =======================================
         8       1    0x219b0  in_block < main

以下示例显示了一个典型的详细泄漏报告。

Actual leaks report    (actual leaks:         3  total size:    2427 bytes)

Memory Leak (mel):
Found 2 leaked blocks with total size 1852 bytes
At time of each allocation, the call stack was:
    [1] true_leak() at line 220 in "leaks.c"
    [2] true_leak() at line 224 in "leaks.c"

Memory Leak (mel):
Found leaked block of size 575 bytes at address 0x22150
At time of allocation, the call stack was:
    [1] true_leak() at line 220 in "leaks.c"
    [2] main() at line 87 in "leaks.c"

Possible leaks report  (possible leaks:       1  total size:       8 bytes)

Possible memory leak -- address in block (aib):
Found leaked block of size 8 bytes at address 0x219b0
At time of allocation, the call stack was:
    [1] in_block() at line 177 in "leaks.c"
    [2] main() at line 100 in "leaks.c"

生成泄漏报告

您可以使用 showleaks 命令随时查看泄漏报告,该报告将报告自上次执行 showleaks 命令后的新内存泄漏。 有关更多信息,请参见showleaks 命令

组合泄漏

由于单个泄漏的数量可能会非常大,因此运行时检查会自动将同一位置分配的泄漏合并到一个合并泄漏报告中。决定是组合泄漏、还是分别报告,这是由 number-of-frames-to-match 参数控制的,您可在 check –leaks 上的 -match m 选项或 showleaks 命令的 -m 选项中指定该参数。如果两个或更多泄漏分配时的调用堆栈与 m 帧在严格程序计数器等级匹配,便会在一个合并泄漏报告中报告这些泄漏。

假设有下列三个调用序列:

块 1
块 2
块 3
[1] malloc
[1] malloc
[1] malloc
[2] d() at 0x20000
[2] d() at 0x20000
[2] d() at 0x20000
[3] c() at 0x30000
[3] c() at 0x30000
[3] c() at 0x31000
[4] b() at 0x40000
[4] b() at 0x41000
[4] b() at 0x40000
[5] a() at 0x50000
[5] a() at 0x50000
[5] a() at 0x50000

如果所有这些块均导致内存泄漏,则 m 值决定泄漏按单独泄漏还是一个重复泄漏来报告。如果 m 为 2,则块 1 和块 2 按一个重复泄漏来报告,因为两个调用序列 malloc() 上的 2 个堆栈帧相同。块 3 将按单独泄漏来报告,因为 c() 的跟踪与其他块不匹配。如果 m 大于 2,则运行时检查将所有泄漏报告为单独的泄漏。malloc 不会显示在泄漏报告中。

一般情况下,m 值越小,生成的单个泄漏报告越少,合并泄漏报告越多。m 值越大,生成的合并泄漏报告越少,单个泄漏报告越多。

修复内存泄漏

获得内存泄漏报告后,按下列指导修复内存泄漏:

  • 最重要的是确定泄漏位置。泄漏报告会提供泄漏块的分配跟踪,即泄漏块的分配位置。

  • 然后可以查看程序的执行流程,了解块的使用情况。如果指针丢失位置很明显,便很好解决;否则,可以使用 showleaks 来缩小泄漏时段。缺省情况下,showleaks 命令仅列出自上次执行 showleaks 命令后创建的新泄漏。 可以在单步执行程序的同时重复运行 showleaks 以缩小内存块泄漏时段。

有关更多信息,请参见showleaks 命令