Sun Studio 12 Update 1:使用 dbx 调试程序

使用内存泄漏检查

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

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

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


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.*/
}

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

有时,内存泄漏一词用于指未释放的内存块。内存泄漏的这一定义很少用到,因为常见的编程惯例是,如果程序不久便会终止,就不释放内存。如果程序仍然有一个或多个指向内存块的指针,运行时检查不会将内存块按泄漏来报告。

检测内存泄漏错误

运行时检查检测下列内存泄漏错误:

可能的泄漏

在两种情况下,运行时检查会报告“可能的”泄漏。第一种情况是没有指针指向块开始处,但有指针指向块内部时。这种情况按“地址位于块内 (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 中的局部变量的堆块,因为程序即将终止并从 main 返回,而不必调用 exit()。要防止运行时检查将这种块按内存泄漏来报告,请在 main 中最后一个可执行源代码行设置一个断点,以在 main 返回前停止程序。当程序在该处停止时,使用 showleaks 命令报告所有真正的泄漏,而忽略仅由 main 中的变量超出作用域导致的泄漏。

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

理解内存泄漏报告

在启用了泄漏检查时,程序退出时会自动生成泄漏报告。所有可能的泄漏都会报告出来,但前提是程序不是使用 kill 命令中止的。报告中的详细程度由 dbx 环境变量 rtc_mel_at_exit 控制(请参见设置 dbx 环境变量)。缺省情况下,会生成简短的泄漏报告。

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

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

信息  

说明  

大小 

泄漏块的大小。 

位置 

泄漏块被分配到的位置。 

地址 

泄漏块的地址。 

栈 

分配时调用栈,由 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 命令