内存泄漏检测应该使用vsize、size和rss中的哪一个?

Which of vsize, size and rss should be used in memory leak detection?

vsizesizerss 三个值中的哪一个来自 ps 输出是否适合用于快速内存泄漏检测?出于我的目的,如果一个进程已经 运行ning 几天并且它的内存一直在增加,那么这足以表明它正在泄漏内存。我知道最终应该使用像 valgrind 这样的工具,但它的使用是侵入性的,因此并不总是可取的。

根据我的理解,我编写了一段简单的 C 代码,基本上分配 1 MiB 内存,释放它,然后再次分配 1 MiB。它还会在每一步前休眠 10 秒,让我有时间查看 ps -p <pid> -ovsize=,size=,rss= 的输出。这是:

#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>

#define info(args...) printf(args)

char* bytes(char* str, uint32_t size, uint32_t n)
{
    char* unit = "B";

    if (n > 1000) {
        n /= 1000;
        unit = "KB";
    }
    if (n > 1000) {
        n /= 1000;
        unit = "MB";
    }

    snprintf(str, size, "%u %s", n, unit);
    return(str);
}

void* xmalloc(size_t size)
{
    char msg[64];
    size_t max = sizeof(msg);
    void *p = NULL;

    info("Allocating %s\n", bytes(msg, max, size));
    p = malloc(size);
    memset(p, '1', size);
    return(p);
}


void* xfree(void* p, size_t size)
{
    char msg[64];
    size_t max = sizeof(msg);
    info("Freeing %s\n", bytes(msg, max, size));
    free(p);
    return(NULL);
}
void nap()
{
    const int dur = 10;
    info("Sleeping for %d seconds\n", dur);
    sleep(dur);
}

int main(void)
{
    int err = 0;
    size_t kb = 1024;
    size_t block = 1024 * kb;
    char* p = NULL;

    nap();
    p = xmalloc(block);
    nap();
    p = xfree(p, block);
    nap();
    p = xmalloc(block);
    nap();

    return(err);
}

现在,ps 每两秒从一个 shell 脚本开始 运行,该脚本还有助于打印测量时间戳。它的输出是:

# time vsize size rss
1429207116   3940   188   312
1429207118   3940   188   312
1429207120   3940   188   312
1429207122   3940   188   312
1429207124   3940   188   312
1429207126   4968  1216  1364
1429207128   4968  1216  1364
1429207130   4968  1216  1364
1429207132   4968  1216  1364
1429207135   4968  1216  1364
1429207137   3940   188   488
1429207139   3940   188   488
1429207141   3940   188   488
1429207143   3940   188   488
1429207145   5096  1344  1276
1429207147   5096  1344  1276
1429207149   5096  1344  1276
1429207151   5096  1344  1276
1429207153   5096  1344  1276

根据上面的值,并牢记 ps(1) 的手册页中给出的描述,在我看来最好的衡量标准是 vsize。这种理解是否正确?请注意,手册页说 size 是脏页总数的度量,而 rss 是物理内存中的页数。这些可能会大大低于进程使用的总内存。

这些实验是在 Debian 7.8 运行ning GNU/Linux 3.2.0-4-amd64.

上进行的

一般来说,进程的总虚拟大小 (vsize) 是衡量进程大小的主要指标。 rss 只是此时恰好在使用实际内存的部分。 size 衡量实际修改了多少页。

不断增加的 vsize,具有相对稳定或循环的 sizerss 值可能表明堆碎片或糟糕的堆分配器算法。

不断增加的 vsizesize,以及相对稳定的 rss 可能表明存在内存泄漏、堆碎片或糟糕的堆分配器算法。

您必须了解给定程序如何使用内存资源,以便仅使用这些进程资源使用的外部度量来估计它是否遭受内存泄漏。

其中一部分涉及了解 C 库 malloc()free() 例程如何管理堆,包括内部可能需要哪些额外内存来管理活动列表分配,它如何处理堆碎片,以及它如何将未使用的堆部分释放回操作系统。

例如,您的测试表明,进程的总虚拟大小及其所需的 "dirty" 页数在程序第二次再次分配相同数量的内存时略有增加。这可能显示了 malloc() 的一些开销,即到那时它自己的内部数据结构所需的内存量。如果程序在退出前再执行一次 free()sleep(),看看会发生什么会很有趣。修改您的代码,使其在调用 malloc()memset() 之间调用 sleep(),然后观察 ps.

的结果也可能具有指导意义。

所以,一个简单的程序应该只需要固定数量的内存到 运行,或者分配内存来完成特定的工作单元,然后应该在完成该工作单元后释放所有内存已完成,应该显示出相对稳定的 vsize,假设它不会一次处理超过一个工作单元并且具有 "bad" 分配模式,这会导致堆碎片。

正如您所指出的,像 valgrind 这样的工具,以及对程序内部实现的深入了解,对于显示实际的内存泄漏并证明它们完全是程序的责任是必要的。

(顺便说一句,您可能想稍微简化您的代码——不要使用不必要的宏,特别是 info(),对于这种类型的示例,尝试以更大的单位打印值,使用额外的变量来计算大小等,也更像是一种混淆而不是帮助。太多的 printfs 也会混淆代码——只使用那些你需要查看程序在哪一步并查看不是的值在编译时已知。)