为什么realloc正在消耗大量内存?

为什么realloc正在消耗大量内存?,c,malloc,realloc,C,Malloc,Realloc,由于源代码的原因,这个问题有点长,我尽量简化源代码。请耐心听我说,谢谢你一直阅读。 我有一个应用程序,其循环可能会运行数百万次。我想先做一次malloc调用,然后再做数千到数百万次realloc调用,而不是在该循环中进行数千到数百万次malloc调用 但是我遇到了一个问题,当我使用realloc时,我的应用程序会消耗几GB的内存并导致自身死亡。如果我使用malloc,我的内存使用情况很好 如果我使用valgrind的memtest在较小的测试数据集上运行,它会使用malloc或realloc报告

由于源代码的原因,这个问题有点长,我尽量简化源代码。请耐心听我说,谢谢你一直阅读。

我有一个应用程序,其循环可能会运行数百万次。我想先做一次
malloc
调用,然后再做数千到数百万次
realloc
调用,而不是在该循环中进行数千到数百万次
malloc
调用

但是我遇到了一个问题,当我使用
realloc
时,我的应用程序会消耗几GB的内存并导致自身死亡。如果我使用
malloc
,我的内存使用情况很好

如果我使用
valgrind
的memtest在较小的测试数据集上运行,它会使用
malloc
realloc
报告没有内存泄漏

我已验证我正在将每个
malloc
-ed(然后是
realloc
-ed)对象与相应的
free
匹配

因此,从理论上讲,我并没有泄漏内存,只是使用
realloc
似乎消耗了我所有可用的RAM,我想知道为什么以及我能做些什么来修复这个问题

我最初使用的是类似这样的东西,它使用
malloc
并正常工作:

Malloc代码

void A () {
    do {
        B();
    } while (someConditionThatIsTrueForMillionInstances);
}

void B () {
    char *firstString = NULL;
    char *secondString = NULL;
    char *someOtherString;

    /* populate someOtherString with data from stream, for example */

    C((const char *)someOtherString, &firstString, &secondString);

    fprintf(stderr, "first: [%s] | second: [%s]\n", firstString, secondString);

    if (firstString)
        free(firstString);
    if (secondString)
        free(secondString);
}

void C (const char *someOtherString, char **firstString, char **secondString) {
    char firstBuffer[BUFLENGTH];
    char secondBuffer[BUFLENGTH];

    /* populate buffers with some data from tokenizing someOtherString in a special way */

    *firstString = malloc(strlen(firstBuffer)+1);
    strncpy(*firstString, firstBuffer, strlen(firstBuffer)+1);

    *secondString = malloc(strlen(secondBuffer)+1);
    strncpy(*secondString, secondBuffer, strlen(secondBuffer)+1);
}
void A () {
    char *firstString = NULL;
    char *secondString = NULL;

    do {
        B(&firstString, &secondString);
    } while (someConditionThatIsTrueForMillionInstances);

    if (firstString)
        free(firstString);
    if (secondString)
        free(secondString);
}

void B (char **firstString, char **secondString) {
    char *someOtherString;

    /* populate someOtherString with data from stream, for example */

    C((const char *)someOtherString, &(*firstString), &(*secondString));

    fprintf(stderr, "first: [%s] | second: [%s]\n", *firstString, *secondString);
}

void C (const char *someOtherString, char **firstString, char **secondString) {
    char firstBuffer[BUFLENGTH];
    char secondBuffer[BUFLENGTH];

    /* populate buffers with some data from tokenizing someOtherString in a special way */

    /* realloc should act as malloc on first pass through */

    *firstString = realloc(*firstString, strlen(firstBuffer)+1);
    strncpy(*firstString, firstBuffer, strlen(firstBuffer)+1);

    *secondString = realloc(*secondString, strlen(secondBuffer)+1);
    strncpy(*secondString, secondBuffer, strlen(secondBuffer)+1);
}
这个很好用。但是我想要更快的

现在我测试一个
realloc
排列,它
malloc
-s只测试一次:

Realloc代码

void A () {
    do {
        B();
    } while (someConditionThatIsTrueForMillionInstances);
}

void B () {
    char *firstString = NULL;
    char *secondString = NULL;
    char *someOtherString;

    /* populate someOtherString with data from stream, for example */

    C((const char *)someOtherString, &firstString, &secondString);

    fprintf(stderr, "first: [%s] | second: [%s]\n", firstString, secondString);

    if (firstString)
        free(firstString);
    if (secondString)
        free(secondString);
}

void C (const char *someOtherString, char **firstString, char **secondString) {
    char firstBuffer[BUFLENGTH];
    char secondBuffer[BUFLENGTH];

    /* populate buffers with some data from tokenizing someOtherString in a special way */

    *firstString = malloc(strlen(firstBuffer)+1);
    strncpy(*firstString, firstBuffer, strlen(firstBuffer)+1);

    *secondString = malloc(strlen(secondBuffer)+1);
    strncpy(*secondString, secondBuffer, strlen(secondBuffer)+1);
}
void A () {
    char *firstString = NULL;
    char *secondString = NULL;

    do {
        B(&firstString, &secondString);
    } while (someConditionThatIsTrueForMillionInstances);

    if (firstString)
        free(firstString);
    if (secondString)
        free(secondString);
}

void B (char **firstString, char **secondString) {
    char *someOtherString;

    /* populate someOtherString with data from stream, for example */

    C((const char *)someOtherString, &(*firstString), &(*secondString));

    fprintf(stderr, "first: [%s] | second: [%s]\n", *firstString, *secondString);
}

void C (const char *someOtherString, char **firstString, char **secondString) {
    char firstBuffer[BUFLENGTH];
    char secondBuffer[BUFLENGTH];

    /* populate buffers with some data from tokenizing someOtherString in a special way */

    /* realloc should act as malloc on first pass through */

    *firstString = realloc(*firstString, strlen(firstBuffer)+1);
    strncpy(*firstString, firstBuffer, strlen(firstBuffer)+1);

    *secondString = realloc(*secondString, strlen(secondBuffer)+1);
    strncpy(*secondString, secondBuffer, strlen(secondBuffer)+1);
}
如果我在命令行上查看
free-m
的输出,同时使用导致百万循环的大数据集运行基于
realloc
的测试,我的内存从4GB下降到0,应用程序崩溃


使用导致此问题的
realloc
我缺少什么?如果这是一个愚蠢的问题,请道歉,并提前感谢您的建议。

realloc
如果无法执行调整大小操作,则必须将内容从旧缓冲区复制到新缓冲区。如果不需要保留原始内存,则
malloc
/
free
对可能比
realloc
对更好

这就是为什么
realloc
暂时需要比
malloc
/
空闲
对更多内存的原因。您还通过不断交错
realloc
s来鼓励碎片化。也就是说,你基本上在做:

malloc(A);
malloc(B);

while (...)
{
    malloc(A_temp);
    free(A);
    A= A_temp;
    malloc(B_temp);
    free(B);
    B= B_temp;
}
鉴于原始代码:

while (...)
{
    malloc(A);
    malloc(B);
    free(A);
    free(B);
}
在第二个循环的每一个循环结束时,您已经清除了所有使用的内存;这比在不完全释放所有内存分配的情况下交错内存分配更有可能将全局内存堆返回到干净状态。

您希望
&(*firstString)
firstString
相同,但实际上,它是将参数的地址带到函数中,而不是通过
A
中指针的地址。因此,每次调用时,都会复制NULL、realloc新内存,丢失指向新内存的指针,然后重复。您可以通过查看
A
末尾的原始指针仍然为空来轻松验证这一点


编辑:好吧,这是一个很棒的理论,但我对我可以测试的编译器似乎是错的。

当你不想保留内存块的现有内容时,使用
realloc
是一个非常糟糕的主意。如果没有其他内容,您将浪费大量时间复制即将覆盖的数据。实际上,按照您使用它的方式,调整大小的块将无法容纳在旧空间中,因此它们位于堆上越来越高的地址,从而导致堆增长得很可笑


内存管理并不容易。糟糕的分配策略会导致碎片、糟糕的性能等。您所能做的就是避免引入任何超出您绝对需要的约束(如在不需要时使用
realloc
),在使用完后尽可能释放内存,并在一次分配中同时分配大块的相关数据,而不是一小块一小块地分配。

您是否也可以显示strace的输出?了解系统调用realloc vs malloc/free映射到什么上会很有启发性。您可能希望了解一个自定义分配函数,它基本上通过malloc()获取一个巨大的块,并将其作为自定义堆空间进行管理。这有点老套,但如果你知道你在做什么,它可以很好地工作。有没有一种方法可以定期刷新碎片堆,比如说每x次迭代?@Alex,这取决于实际的分配器。请注意,我的碎片化想法并不十分精确;这实际上取决于底层实现。如果您使用的是Windows,您可以尝试在全局堆上使用
HeapCompact
,但这并不能保证能起到任何作用。嗯,整个碎片问题似乎与我听说和读到的建议如何使用
realloc
相矛盾,那里有很多昂贵的
malloc
/
免费电话。也许我应该查看堆栈上的固定大小缓冲区:(@Alex:跟踪
firstString
secondString
的当前分配大小。如果它们需要增长,只需麻烦
realloc
它们,并量化它们的大小(例如,使它们成为比您需要的大小大两倍的次幂).@Alex,同样,碎片化的想法并不那么精确;它实际上取决于分配器。使用一个非常简单的基于堆栈的分配器(即,它只回收连续的内存,并以分配的区域结束),使用基于realloc的策略,您可能会出现病态的错误分配行为。在调整为常规模式的其他分配器上,您可能不会。这实际上取决于分配器。但realloc通常在最坏的情况下需要旧的大小加上新的大小。我很抱歉