继续调用 malloc() 和 free() 有多糟糕?

Posted

技术标签:

【中文标题】继续调用 malloc() 和 free() 有多糟糕?【英文标题】:How bad it is to keep calling malloc() and free()? 【发布时间】:2011-11-28 13:31:37 【问题描述】:

我正在发送一个文本文件 - 客户端-服务器 将文本分解成每个 512 字节的数据包 但是有些数据包包含的文本小于最大大小,所以在服务器端接收每个数据包时,我正在调用 malloc() 再次构建字符串,这是一种不好的做法吗? 保留一个适合最大长度的工作缓冲区并继续迭代、复制和覆盖其值是否更好?

好的@n.m。这是代码,这个 if 在一个 for(;;) 循环中,由 select() 唤醒

if(nbytes==2) 
            packet_size=unpack_short(short_buf);
            printf("packet size is %d\n",packet_size);
            receive_packet(i,packet_size,&buffer);
            printf("packet=%s\n",buffer);
            free(buffer);

//and here is receive_packet() function 
int receive_packet(int fd,int p_len,char **string) 
 *string = (char *)malloc(p_len-2); // 2 bytes for saving the length    
 char *i=*string;   
 int temp;
 int total=0;
 int remaining=p_len-2;
 while(remaining>0) 
     //printf("remaining=%d\n",remaining);
     temp = recv(fd,*string,remaining,0);
     total+=temp;
     remaining=(p_len-2)-total;
     (*string) += temp;
 
 *string=i;
 return 0;
 

【问题讨论】:

一行代码值一千个cmets。 @n.m.我将代码添加到主帖 【参考方案1】:

在您的示例中,您的函数已经包含一个系统调用,因此malloc/free 的相对成本几乎无法衡量。在我的系统上,malloc/free“往返”平均大约 300 个周期,而 最便宜 系统调用(获取当前时间、pid 等)至少需要 2500 个周期。预计recv 的成本很容易达到 10 倍,在这种情况下,分配/释放内存的成本最多约为该操作总成本的 1%。

当然,确切的时间会有所不同,但粗略的数量级在不同系统之间应该是相当不变的。我什至不会开始考虑删除 malloc/free 作为优化,除非在纯用户空间的函数中。没有动态分配可能实际上更有价值的是在不应该有失败情况的操作中 - 这里的价值是你简化硬化你的代码,而不必担心malloc失败时该怎么办。

【讨论】:

我很难相信你的 malloc/free 只需要 300 个周期。您是否使用碎片堆并包括需要从操作系统引入新内存并释放它的 malloc? 我正在计时 free(malloc(1));(在 Linux/glibc/i686 上,使用 rdtsc 测量),它不需要从操作系统映射新内存,只需重用现有的先前释放的内存。这几乎总是常见的情况。您唯一需要担心从操作系统获取新内存的时间是在实时编程中,您关心的是任何操作的最坏情况延迟,而不是程序的整体运行时间。 使用初始化为 NULL 的 10-100 个分配的数组和随机大小的 malloc/free 随机分配槽会是一个更好的测试。 好的。有趣的东西。这是我为 16 个随机插槽分配最多 16384 个字节的结果: tsc 平均循环 = 247 tsc 最长循环 = 833292 对于 512 个插槽,每个插槽最多 163840 字节:tsc 平均循环 = 408 tsc 最长循环 = 294350【参考方案2】:

调用 malloc 和 free 会产生开销。当您释放 revese 时,必须从堆中分配一个块并将其标记为正在使用。不知道您使用的是什么操作系统或编译器,这可能在 c 库或操作系统内存管理级别。由于您正在执行大量 malloc 和释放操作,因此您可能最终会严重分割您的堆,而您可能没有足够的连续空闲内存来在其他地方执行 malloc。如果您可以只分配一个缓冲区并继续重复使用它,那通常会更快,并且堆碎片的风险更小。

【讨论】:

如果他一遍又一遍地分配和释放一个固定大小的块,他几乎肯定不会分割任何东西。 (这并不意味着没有开销......但在这种情况下,碎片可能不是导致它的原因。) 假设程序中没有其他东西从堆中分配内存,并且它是单线程的,我同意。它可能每次都返回相同的内存块。但是,我们不知道程序中还有什么是从堆中分配的,也不知道这个特定的堆管理器是如何实现的。 考虑到屏幕保护程序的最初目的,如果我们在这里谈论一个长时间运行的程序,那么似乎 在硬件上进行 malloc 和在函数中释放一个数组,该函数被调用无数次,而不是 malloc 并仅全局释放一次。坦率地说,我不知道这是否是一个有效的考虑...我知道存在硬件恶意代码,但我怀疑全局 malloc 不属于该类别:)【参考方案3】:

我发现 malloc、realloc 和 free 非常昂贵。如果可以避免 malloc,最好重用已有的内存。

编辑: 看起来我对 malloc 的昂贵程度是错误的。在 Linux 上使用 GNU C 库 2.14 版进行的一些计时测试表明,对于循环 100,000 次并分配和释放 512 个随机大小从 1 到 163840 字节的插槽的测试:

tsc average loop = 408
tsc of longest loop = 294350

因此,在紧密的内部循环中浪费 408 个循环来执行 mallocnew 将是一件愚蠢的事情。除此之外,别担心。

【讨论】:

【参考方案4】:

Malloc 通常相当便宜。如果它生成一个系统调用来获得更多的堆空间,它只会很昂贵。例如,在类 UNIX 系统中,它最终会生成一个昂贵的 sbrk 调用。如果你反复 malloc 和释放相同大小的内存,它会做的很快。例如,考虑以下小测试程序:

#include <stdlib.h>


int main()

  int i=0;
  int *ptr;

  for(int i=0; i<1e6; i++) 
    ptr = malloc(1024*sizeof(int));
    free(ptr);
  

它分配 1024 个整数并释放它们并执行一百万次。在我相当普通的小 chromebook 变成 linux 机器上运行它,我得到的时间看起来像这样:

time ./test

real    0m0.125s
user    0m0.122s
sys     0m0.003s

现在,如果我注释掉循环的 malloc 和 free 部分,我会得到这些时间:

time ./test

real    0m0.009s
user    0m0.005s
sys 0m0.005s

所以你看到 malloc 和 free 确实有开销,尽管我认为 10 倍于什么都不做的开销是非常多的开销。

如果它可以一遍又一遍地重复使用同一个堆块(就像这里的情况一样),它会特别快。当然,如果我不断重复分配和增长程序,则需要更多时间,因为这会导致一些系统调用。

当然,根据操作系统、编译器和 stdlib 实现,您的工作量可能会有所不同。

【讨论】:

【参考方案5】:

如果传递给 malloc 的大小是可变的,则调用多个 malloc/free 实际上可以增加您的进程使用的内存(不涉及任何泄漏),正如这个问题所证明的那样:

C program memory usage - more memory reported than allocated

所以单缓冲方法可能是最好的。

【讨论】:

【参考方案6】:

只有测试才能判断。不过,在使用 C 语言编程时,我会在避免 malloc 方面犯错,因为如果您不小心创建了内存泄漏,则可能很难修复。

【讨论】:

【参考方案7】:

衡量两种解决方案的性能。通过分析或测量吞吐量。不能肯定地说什么。

【讨论】:

以上是关于继续调用 malloc() 和 free() 有多糟糕?的主要内容,如果未能解决你的问题,请参考以下文章

malloc 和 free 是如何实现的?

malloc/free 的使用要点

malloc/free 不会调用类的构造/析构函数

malloc必须有对应的free

严格来说,C 标准是不是要求 free 必须在 malloc 之后调用,当不满足这个要求时它规定了啥?

有关于malloc申请内存和free内存释放