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



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

为了便于理解,我写了一段简单的C代码,基本上分配1MIB的内存,释放它,然后再次分配1MIB。它还在每一步之前休眠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 %sn", 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 %sn", bytes(msg, max, size));
free(p);
return(NULL);
}
void nap()
{
const int dur = 10;
info("Sleeping for %d secondsn", 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)是衡量流程大小的主要指标。CCD_ 6只是目前正在使用实际内存的部分。size是衡量实际修改了多少页面的指标。

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

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

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

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

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

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

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

(顺便说一句,你可能想在一定程度上简化你的代码——尤其是不要使用info()这样不必要的宏,对于这种类型的例子,试图以更大的单位打印值,使用额外的变量来进行大小计算等,也更像是一种混淆,而不是一种帮助。太多的printfs也会混淆代码——只使用那些你需要了解程序的步骤的宏以及查看编译时未知的值。)

最新更新