ps输出的vsize、size和rss这三个值中哪一个适合用于快速内存泄漏检测?就我的目的而言,如果一个进程已经运行了几天,并且它的内存一直在增加,那么这就足以表明它正在泄漏内存。我知道像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。这种理解正确吗?注意,手册页上说大小是脏页总量的度量,rss是物理内存中的页总量。这些内存可能会大大低于进程使用的总内存。
这些实验是在运行gnu/linux 3.2.0-4-amd64的debian 7.8上进行的。

最佳答案

一般来说,流程的总虚拟大小(vsize)是衡量流程大小的主要指标。rss只是目前碰巧在使用真实内存的部分。size是实际修改了多少页的度量。
持续增加的vsize值、相对稳定或循环的size值和rss值可能表示堆碎片或堆分配器算法不好。
持续增加的vsizesize,以及相对稳定的rss可能意味着内存泄漏、堆碎片或堆分配器算法不好。
您必须了解给定程序如何使用内存资源,以便仅使用这些进程资源使用的外部度量来估计它是否遭受内存泄漏。
其中一部分涉及到了解一点堆是如何由c库malloc()free()例程管理的,包括管理活动分配列表可能需要多少额外内存,如何处理堆的碎片,以及如何将堆的未使用部分释放回操作系统。
例如,您的测试显示,当程序再次分配相同的内存量时,进程的总虚拟大小和它所需的“脏”页的数量都略有增加。这可能显示了malloc()的一些开销,即到那时它自己的内部数据结构所需的内存量。如果程序在退出之前做了另一个free()sleep(),那会有什么意思。修改代码以便在调用sleep()malloc()之间调用memset(),然后观察ps的结果,这可能也是有指导意义的。
因此,如果一个简单的程序只需要一个固定数量的内存来运行,或者分配内存来完成一个特定的工作单元,然后在该工作单元完成后释放所有的内存,那么它应该显示一个相对稳定的vsize,假设它一次处理的工作单元不超过一个,并且有一个会导致堆碎片的分配。
正如您所指出的,像valgrind这样的工具,以及对程序内部实现的深入了解,对于显示实际的内存泄漏并证明它们完全是程序的责任是必要的。
(顺便说一句,您可能希望稍微简化代码——特别是不要使用不必要的宏,比如info(),对于这种类型的示例,尝试以更大的单位打印值、使用额外的变量进行大小计算等等,也是一种迷惑,而不是帮助。过多的printfs也会使代码变得模糊——只使用那些需要查看程序执行的步骤以及查看编译时未知值的步骤。)

07-24 09:47
查看更多