由于源代码,这个问题有点长,我试图尽可能简化。请耐心等待,并感谢您的阅读。
我的应用程序循环可能运行数百万次。我想在该循环中进行几千到几百万次malloc
/ free
次呼叫,而不是预先进行一次malloc
,然后进行几千到几百万realloc
次呼叫。
但是当我使用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);
}
这很好用。但我想要更快的东西。
现在我测试一个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
我错过了什么?对不起,如果这是一个愚蠢的问题,并提前感谢您的建议。
答案 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
末尾的原始指针仍为空来轻松验证这一点。
编辑:嗯,这是一个很棒的理论,但我似乎在我可以测试的编译器上出错了。