为什么realloc吃了大量的记忆?

时间:2010-11-15 23:49:47

标签: c malloc realloc

由于源代码,这个问题有点长,我试图尽可能简化。请耐心等待,并感谢您的阅读。

我的应用程序循环可能运行数百万次。我想在该循环中进行几千到几百万次malloc / free次呼叫,而不是预先进行一次malloc,然后进行几千到几百万realloc次呼叫。

但是当我使用realloc时,我遇到了一个问题,即我的应用程序消耗了几GB的内存并自行终止。如果我使用malloc,我的内存使用情况就好了。

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

我已经确认我将每个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);
}

这很好用。但我想要更快的东西。

现在我测试一个realloc安排,malloc - 只有一次:

Realloc代码

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的测试,我的内存从4开始GB降至0,应用程序崩溃。

使用导致此问题的realloc我错过了什么?对不起,如果这是一个愚蠢的问题,并提前感谢您的建议。

3 个答案:

答案 0 :(得分:8)

如果无法在适当的位置进行调整大小操作,则

realloc必须将内容从旧缓冲区复制到新缓冲区。如果您不需要保留原始内存,则malloc / free对可能优于realloc

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

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);
}

在每个第二个循环结束时,你已经清理了你使用的所有内存;这种情况更有可能将全局内存堆返回到干净状态,而不是通过交错内存分配而不完全释放所有内存堆。

答案 1 :(得分:1)

当您不想保留内存块的现有内容时使用realloc非常糟糕的主意。如果不出意外,您将浪费大量时间来复制您即将覆盖的数据。实际上,你使用它的方式,调整大小的块将不适合旧空间,因此它们位于堆上逐渐越来越高的地址,导致堆变得荒谬可笑。

内存管理并不容易。错误的分配策略会导致碎片化,恶劣的性能等等。您可以做的最好的事情就是避免引入任何超出您必须的限制(例如在不需要时使用realloc),尽可能多地释放内存完成它,并在一个分配中分配大块相关数据,而不是小块。

答案 2 :(得分:0)

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

编辑:嗯,这是一个很棒的理论,但我似乎在我可以测试的编译器上出错了。