11

我有一个图像压缩应用程序,它现在有两个不同版本的内存分配系统。在最初的版本中,malloc 无处不在,而在第二个版本中,我实现了一个简单的池分配器,它只分配一块内存并将部分内存返回给 myalloc() 调用。

我们注意到使用 malloc 时内存开销巨大:在内存使用高峰期,malloc() 代码需要大约 170 兆字节的内存来处理 1920x1080x16bpp 的图像,而池分配器仅分配 48 兆字节,其中 47被程序使用。

在内存分配模式方面,程序为测试图像分配了大量的 8 字节(大多数)、32 字节(很多)和 1080 字节(一些)的块。除此之外,代码中没有动态内存分配。

测试系统操作系统为Windows 7(64位)。

我们如何测试内存使用情况?

使用自定义分配器,我们可以看到使用了多少内存,因为所有 malloc 调用都被推迟到分配器。使用 malloc(),在调试模式下,我们只是单步执行代码并在任务管理器中观察内存使用情况。在发布模式下,我们做了同样的事情,但粒度不那么细,因为编译器优化了很多东西,所以我们不能逐段执行代码(发布和调试之间的内存差异大约是 20MB,我将其归因于优化和发布模式下缺少调试信息)。

malloc 是否会导致如此巨大的开销?如果是这样,究竟是什么导致了 malloc 内部的这种开销?

4

3 回答 3

8

在 Windows 7 上,您将始终获得低碎片堆分配器,而无需显式调用 HeapSetInformation() 来请求它。该分配器会牺牲虚拟内存空间来减少碎片。您的程序实际上并没有使用 170 兆字节,您只是看到一堆空闲块在周围等待分配类似大小的空间。

这个算法容易被一个自定义的分配器打败,它不做任何事情来减少碎片。这可能对您很有效,尽管在您保持程序运行时间超过单个调试会话之前,您不会看到它的副作用。如果这是预期的使用模式,您确实需要确保它在数天或数周内保持稳定。

最好的办法就是不要担心它,170 MB 是相当小的土豆。请记住,这是虚拟内存,它不需要任何成本。

于 2012-10-25T09:42:12.557 回答
6

首先 malloc 将指针对齐到 16 字节边界。此外,它们在返回值之前的地址中存储至少一个指针(或分配的长度)。然后他们可能会添加一个神奇的值或释放计数器来指示链表没有损坏或内存块没有被释放两次(free ASSERTS for double frees)。

#include <stdlib.h>
#include <stdio.h>

int main(int ac, char**av)
{
  int *foo = malloc(4);
  int *bar = malloc(4);
  printf("%d\n", (int)bar - (int)foo);
}

返回:32

于 2012-10-25T08:51:37.533 回答
4

注意:当您在 Visual Studio 中运行程序或附加任何调试器时,默认情况下 malloc 行为发生了很大变化,不使用低碎片堆,内存开销可能不代表实际使用情况(另请参见https:// /stackoverflow.com/a/3768820/16673)。您需要使用环境变量 _NO_DEBUG_HEAP=1 来避免受到此影响,或者在不在调试器下运行时测量内存使用情况。

于 2012-10-25T09:40:09.257 回答